sqlite: 动态列类型
环境:
- .net8
- Microsoft.Data.Sqlite 9.0.9
- sqlite 3.50.3
参考:
《官网: Datatypes In SQLite》
《官网: STRICT Tables》
一、动态列类型与严格表
默认情况下,我们创建的表都是非严格表。
对于非严格表: sqlite 不像其他数据库一样(mysql sqlserver pgsql) 每一列都有固定的列类型,sqlite 会根据插入的具体数据决定怎么存储(都能存进去)。即:我们建表时指定的 int,datetime, varchar(50) 等都不会对插入数据产生强约束,即:我们将 ‘abc’ 插入到 int 的列中也是可以的。
create table Example(id int)
insert into Example(id) values('abc')select id,typeof(id) from Example
-- result:
-- id |typeof(id)|
-- ---+----------+
-- abc|text |
对于严格表: sqlite表现的贴近其他数据库,将 ‘abc’ 插入到 int 列是会报错的,并且列类型(也可称为存储类型)只能是: int,integer,real,text,blob,any
之一
create table Example_strict(id int) strict
-- 报错: SQL Error [19]: [SQLITE_CONSTRAINT_DATATYPE] An insert or update attempted to store a value inconsistent with the column's declared type in a table defined as STRICT (cannot store TEXT value in INT column Example_strict.id)
-- insert into Example_strict(id) values('abc')-- 报错: SQL Error [1]: [SQLITE_ERROR] SQL error or missing database (unknown datatype for Example_strict2.name: "varchar(50)")
-- create table Example_strict2(id int,name varchar(50)) strict
二、存储类型
在sqlite中我们一般不强调列类型,因为一般(非严格表
)情况下,我们定义的列类型也没强约束。sqlite推荐的说法是存储类型
。存储类型
直接和存进去的数据挂钩,同一列的不同行可以有不同的存储类型。
SQLite 的五种存储类:
-
NULL
表示空值,没有实际数据内容。 -
INTEGER
有符号整数,根据数值大小使用不同字节数存储:0、1、2、3、4、6 或 8 字节。 👉 存储空间会根据整数的大小自动调整。 -
REAL
浮点数,使用 8 字节的 IEEE 754 双精度格式存储。 -
TEXT
文本字符串,使用数据库设定的编码方式存储(如 UTF-8、UTF-16BE、UTF-16LE)。 -
BLOB
二进制数据,按原始输入方式存储,不做任何转换。
我们可以通过 typeof()
函数查询数据的存储类型。
2.1 非严格表模式下的存储类型演示
create table t_user(id int,score decimal(18,2),name varchar(4)
) -- 非严格表(没有strict关键字)insert into t_user(id,score,name) values(1,95.5,'小明'),('2','100','小明, 你好啊, 超过4个字符'),('3.5',100,99.99),('SNO-20251010-0001','99.9',NULL);select id,typeof(id),score,typeof(score),name,typeof(name)
from t_user
执行如下:
2.2 严格表模式下的存储类型
此时我们可以认为存储类型和其他数据库的列类型很像了。甚至可以认为存储类型就是其他数据库的列类型(但注意,细节仍有差别,比如sqlite的整数就一个类型,是边长的,而其他数据库都分了好多种)。
三、类型亲和力(非严格表时)
在非严格表模式下,sqlite根据我们建表时声明的类型,会自动匹配一个亲和力(总共有5个:INTEGER
,TEXT
,BLOB
,REAL
,NUMERIC
), 具有不同的亲和力的列在接收实际插入的数据时会优先选择偏好的存储类型。
📝 TEXT 亲和性
sqlite识别方法:如果声明类型包含字符串 “CHAR”、“CLOB” 或 “TEXT”
具有 TEXT 亲和性的列使用以下三种存储类:NULL、TEXT、BLOB。 如果向该列插入数值数据,它会被转换为文本形式后存储。
🔢 INTEGER 亲和性
如果声明类型包含字符串 “INT”
行为与 NUMERIC 亲和性相同。唯一的区别体现在 CAST 表达式中:
-
CAST(4.0 AS INT) → 返回整数 4
-
CAST(4.0 AS NUMERIC) → 返回浮点数 4.0
🌡️ REAL 亲和性
如果声明类型包含字符串 “REAL”、“FLOA” 或 “DOUB”
行为类似于 NUMERIC,但会强制将整数转换为浮点数。
- 内部优化:如果浮点数没有小数部分,SQLite 会将其以整数形式写入磁盘以节省空间,并在读取时自动转换回浮点数。这种优化在 SQL 层面是不可见的,只有通过查看数据库文件的原始位数据才能发现。
📦 BLOB 亲和性
如果声明类型包含字符串 “BLOB”,或者没有指定类型
没有任何类型偏好。SQLite 不会尝试将数据从一种存储类转换为另一种,原样存储。
🧮 NUMERIC 亲和性
如果以上规则都不匹配
NUMERIC 亲和性的列可以使用所有五种存储类:NULL、INTEGER、REAL、TEXT、BLOB。
-
插入文本数据时,如果是合法的整数或浮点字面量,会优先转换为 INTEGER,否则为 REAL。
-
如果文本表示的整数太大,无法用 64 位整数表示,则转换为 REAL。
-
TEXT 与 REAL 之间转换时,只保留前 15 位有效十进制数字。
-
如果文本不是合法的数字字面量,则按 TEXT 存储。
-
十六进制整数(如 ‘0x1A’)不被视为合法数字,会作为 TEXT 存储(为了兼容 SQLite 3.8.6 之前的版本)。
-
如果插入的是浮点数,但可以精确表示为整数,则会转换为 INTEGER。
-
NULL 和 BLOB 不会被转换。
📌 示例: 字符串 ‘3.0e+5’ 虽然看起来像浮点数,但它可以精确表示为整数,因此会被存储为整数 300000,而不是浮点数 300000.0。
四、 列类型、类型亲和力和存储类型的总结
4.1 在非严格表中:
列类型是我们定义的指定的,它只是提供给sqlite做字符串匹配识别,内部识别为一种亲和力,在存储的时候每种亲和力根据偏好选择一种落地的存储类型。
如下:
4.2 在严格表中:
要求我们指定的列类型必须是5中存储类型之一,此时不再有上面那一套识别和转换了(即:没有类型亲和力的说法了),一步到位。