Menu

SQLite 数据类型:存储类与动态类型

SQLite 如何存储值:五种存储类、为什么类型是动态的,以及从 Postgres 或 MySQL 转过来的人常踩的坑。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

只有五种存储类,而不是很多类型

SQLite 把每个值都存储为五种“存储类”之一:

  • NULL:没有值。
  • INTEGER:有符号整数,根据大小占 1 到 8 个字节。
  • REAL:8 字节的 IEEE 浮点数。
  • TEXT:字符串,以数据库的编码存储(通常是 UTF-8)。
  • BLOB:原始字节,完全按你提供的样子存储。

就这些。没有单独的 BOOLEAN,没有 DATETIME,没有 VARCHAR,也没有 DECIMAL。别的数据库有几十种类型;SQLite 只有五种,其他一切都建立在它们之上。

typeof() 会报告每个值实际的存储类。你会看到 integer、real、text、blob。这四个再加上 null,就是 SQLite 知道的全部类型。

类型是动态的

这正是让从 Postgres 或 MySQL 转过来的人吃惊的地方。在 SQLite 中(不使用 STRICT 时),列上声明的类型更像是一个“建议”,而不是一份契约。真正的类型跟着每个值走:

两行都被接受了。id 列在一行中存的是整数,在另一行中存的是文本;body 存了文本和整数。SQLite 乐意在任何列中存储任何存储类的值。

这就是“动态类型”,是一个刻意的设计选择。它让 SQLite 在做原型和写小脚本时很宽容。但这也意味着应用代码中的一个笔误,可能多年来一直悄悄地存入格式不对的数据。如果这种取舍让你不安(对于大多数生产环境的表结构,你确实应该在意),STRICT 表就是答案。我们很快会讲到。

一段话讲清类型亲和性

列上声明的类型并不会被忽略,它会赋予这一列一种“亲和性”。插入值时,如果能干净地转换,SQLite 会尝试把值转换成列的亲和类型。TEXT 列收到数字 42 时会把它存成文本 '42';INTEGER 列收到字符串 '42' 时会把它存成整数 42。如果转换会丢失信息,就保留原来的类型。

第一行:整数 42 被转换成文本 '42',字符串 '100' 被转换成整数 100。第二行:'3.5' 无法无损地转换成 INTEGER,所以保持为文本。亲和性后面有专门的一页来讲,现在只需知道:列类型虽然不强制约束,但仍然会影响存储方式。

布尔值

没有 BOOLEAN 存储类。SQLite 把布尔值存成整数:0 表示假,1 表示真:

SQLite 能识别 TRUE 和 FALSE 关键字(从 SQLite 3.23 开始),它们分别转换为 1 和 0。BOOLEAN 声明会给这一列数值亲和性,但并不会把值限制在 0 和 1:不使用 STRICT 时,你就算插入 'maybe',SQLite 也不会反对。

日期和时间

同样也没有 DATETIME。你可以从三种编码中选一种,SQLite 的日期函数对三种都适用:

  • TEXT,使用 ISO-8601 格式:'2026-04-23 14:30:00'。
  • REAL,存为儒略日数。
  • INTEGER,存为 Unix 时间戳秒数。

ISO-8601 文本是最常见的选择:它作为字符串就能正确排序,人也能直接看懂,而内置函数(date()、time()、datetime()、strftime()、julianday())都接受这种格式。每一列选定一种编码并坚持使用;在同一列里混用多种格式,正是那种半年后会反咬你一口的事。

VARCHAR、CHAR 和其他熟悉的名字

SQLite 接受你在其他数据库中熟悉的类型名:VARCHAR(255)、CHAR(10)、NVARCHAR、DECIMAL(10,2)、DOUBLE、FLOAT、INT、BIGINT、MEDIUMINT。它们都能正常解析,只是会按亲和性规则对应到五种存储类之一。

VARCHAR(255) “不会”强制限制 255 个字符,SQLite 会忽略长度。DECIMAL(10, 2) 也“不会”存储固定精度的小数,它得到的是数值亲和性,以 INTEGER 或 REAL 存储。这些名字存在的唯一目的,是让从其他数据库复制过来的表结构能够运行;它们并不会带来这些名字在别处所代表的约束。

如果你需要对金额做精确的小数运算,就把“分”存成 INTEGER。浮点型 REAL 迟早会在小数点后第三位引入舍入误差。

NULL 也是一种存储类

NULL 不只是“没有值”,它是一个有自己存储类的值,typeof() 也会返回它:

b 的报告结果是 null。这很重要,因为 NULL 不等于任何值,甚至不等于另一个 NULL。b = NULL 永远不会为真,你必须写 b IS NULL。后面讲运算符和 NULL 的那一页会详细介绍,但一切都从这个存储类开始。

用 BLOB 存储字节

BLOB 原样存储原始字节,适合小图片、哈希值、编码后的数据,以及任何既不是文本也不是数字的东西:

x'...' 字面量让你能在 SQL 中用十六进制写 blob;在应用代码中,通常是通过参数传入一个字节数组。对 blob 调用 length() 返回的是字节数,而不是字符数。

一个实用的提醒:SQLite 能存储很大的 blob,但每条涉及这一行的查询都要拉出一个 50 MB 的 blob,速度会很慢。对于大文件,把文件存在磁盘上,在数据库里只保存一个路径。

要点回顾

  • 五种存储类(NULL、INTEGER、REAL、TEXT、BLOB)涵盖了一切。
  • 布尔值是整数;日期是文本、实数或整数(由你选择)。
  • 声明的列类型只是提示,不是契约(除非使用 STRICT)。
  • VARCHAR(255) 之类的写法能被解析,但不会强制执行它们在别处代表的长度或精度。
  • 每当你不确定实际存的是什么时,typeof(value) 就是你的好帮手。

下一篇:类型亲和性

前面一笔带过的“提示”行为,背后有一套精确的规则:根据声明的类型名推导出五种亲和性,并在每次插入时应用。这就是下一页的内容,也是预测 SQLite 实际会如何处理你交给它的值的关键。

常见问题

SQLite 支持哪些数据类型?

SQLite 有五种存储类:NULL、INTEGER、REAL、TEXT 和 BLOB。数据库中的每个值都以其中一种形式存储。为了兼容,CREATE TABLE 也接受 VARCHAR(255)、DATETIME 或 BOOLEAN 这些常见的名字,但存储时它们都会对应到这五种之一。

SQLite 有布尔类型或日期时间类型吗?

没有单独的存储类。布尔值以 INTEGER(0 和 1)存储,不过 SQLite 也认识 TRUE 和 FALSE 关键字。日期和时间可以存成 TEXT(ISO-8601 字符串)、REAL(儒略日数)或 INTEGER(Unix 时间戳秒数):编码方式由你选择,日期函数对这三种都适用。

为什么说 SQLite 的类型是动态的?

在大多数数据库中,声明为 INTEGER 的列会拒绝字符串。而在 SQLite 中(默认情况下),声明的类型只是一个“提示”:真正的类型跟着每个值走,所以如果你插入一个整数,TEXT 列也能存下它。这种灵活性有时很有用,有时却会让你自己挖坑。STRICT 表可以关闭它。

Coddy 编程语言插图

和 Coddy 一起学编程

开始