小巧。快速。可靠。
三选二。

常见问题解答

  1. 如何创建 AUTOINCREMENT 字段?
  2. SQLite 支持哪些数据类型?
  3. SQLite 允许我将字符串插入到 integer 类型的数据库列中!
  4. 为什么 SQLite 不允许我在同一张表的不同行上使用 '0' 和 '0.0' 作为主键?
  5. 多个应用程序或同一应用程序的多个实例可以同时访问同一个数据库文件吗?
  6. SQLite 是线程安全的吗?
  7. 如何列出 SQLite 数据库中包含的所有表/索引
  8. SQLite 数据库是否存在已知的尺寸限制?
  9. SQLite 中 VARCHAR 的最大尺寸是多少?
  10. SQLite 支持 BLOB 类型吗?
  11. 如何在 SQLite 中向现有表添加、删除或重命名列?
  12. 我删除了许多数据,但数据库文件并没有变小。这是 bug 吗?
  13. 我可以在我的商业产品中使用 SQLite 而不支付版税吗?
  14. 如何使用包含嵌入式单引号 (') 字符的字符串文字?
  15. 什么是 SQLITE_SCHEMA 错误,为什么我会遇到它?
  16. 我在编译 SQLite 时收到一些编译器警告。这不是问题吗?它不表示代码质量差吗?
  17. Unicode 字符的区分大小写匹配不起作用。
  18. INSERT 速度非常慢 - 我每秒只能执行几十个 INSERT 操作
  19. 我不小心从我的 SQLite 数据库中删除了一些重要的信息。我该如何恢复它?
  20. 什么是 SQLITE_CORRUPT 错误?数据库“格式错误”是什么意思?为什么我会遇到此错误?
  21. SQLite 支持外键吗?
  22. 如果我在构建 SQLite 时使用 SQLITE_OMIT_... 编译时选项,我会收到编译器错误。
  23. 我的 WHERE 子句表达式column1="column1"不起作用。它会导致返回表的每一行,而不仅仅是 column1 值为 "column1" 的行。
  24. SQLite 的语法图(又称“铁路”图)是如何生成的?
  25. SQL 标准要求即使唯一约束中的一个或多个列为 NULL 也必须强制执行唯一约束,但 SQLite 不会这样做。这不是 bug 吗?
  26. SQLite 的出口管制分类号 (ECCN) 是什么?
  27. 我的查询没有返回我期望的列名。这是 bug 吗?
  28. 我的数据库去哪里了?(或者:我的数据库是如何变成空的?)

(1) 如何创建 AUTOINCREMENT 字段?

简短回答:声明为 INTEGER PRIMARY KEY 的列将自动递增。

较长的回答:如果您将表的列声明为 INTEGER PRIMARY KEY,那么每当您将 NULL 插入到该表的该列时,NULL 将自动转换为一个整数,该整数比该列在表中所有其他行的最大值大 1,或者如果表为空则为 1。或者,如果最大的现有整数键 9223372036854775807 正在使用,则会随机选择一个未使用的键值。例如,假设您有一个这样的表

CREATE TABLE t1(
  a INTEGER PRIMARY KEY,
  b INTEGER
);

使用此表,语句

INSERT INTO t1 VALUES(NULL,123);

在逻辑上等同于说

INSERT INTO t1 VALUES((SELECT max(a) FROM t1)+1,123);

有一个名为 sqlite3_last_insert_rowid() 的函数将返回最近插入操作的整数键。

请注意,整数键比插入前表中最大的键大 1。新键在表中当前的所有键中都是唯一的,但它可能与之前已从表中删除的键重叠。要创建在表生命周期内唯一的键,请将 AUTOINCREMENT 关键字添加到 INTEGER PRIMARY KEY 声明中。然后,选择的键将比该表中曾经存在过的最大键大 1。如果最大的可能键以前存在于该表中,则 INSERT 将失败并出现 SQLITE_FULL 错误代码。

(2) SQLite 支持哪些数据类型?

SQLite 使用 动态类型。内容可以存储为 INTEGER、REAL、TEXT、BLOB 或 NULL。

(3) SQLite 允许我将字符串插入到 integer 类型的数据库列中!

这是一个特性,而不是 bug。SQLite 使用 动态类型。它不强制执行数据类型约束。任何类型的数据都可以(通常)插入到任何列中。您可以在整数列中放置任意长度的字符串,在布尔列中放置浮点数,或在字符列中放置日期。您在 CREATE TABLE 命令中为列分配的 数据类型 不会限制可以放入该列中的数据。每一列都可以保存任意长度的字符串。(有一个例外:类型为 INTEGER PRIMARY KEY 的列只能保存 64 位有符号整数。如果您尝试将整数以外的任何内容放入 INTEGER PRIMARY KEY 列中,则会导致错误。)

但是 SQLite 会使用列的声明类型作为提示,表明您更喜欢该格式的值。因此,例如,如果一个列的类型为 INTEGER,并且您尝试将字符串插入到该列中,SQLite 将尝试将字符串转换为整数。如果可以,它会插入整数。否则,它会插入字符串。此功能称为 类型亲和性

(4) 为什么 SQLite 不允许我在同一张表的不同行上使用 '0' 和 '0.0' 作为主键?

当您的主键为数字类型时会出现此问题。将主键的 数据类型 更改为 TEXT,它应该可以工作。

每一行都必须具有唯一的键。对于具有数字类型的列,SQLite 认为 '0''0.0' 是相同的值,因为它们在数值上比较相等。(参见前一个问题。)因此,这些值不是唯一的。

(5) 多个应用程序或同一应用程序的多个实例可以同时访问同一个数据库文件吗?

多个进程可以同时打开同一个数据库。多个进程可以同时执行 SELECT 操作。但是,在任何时间点,只有一个进程可以对数据库进行更改。

SQLite 使用读写锁来控制对数据库的访问。(在缺少对读写锁支持的 Win95/98/ME 下,会使用概率模拟代替。)但请注意:如果数据库文件保存在 NFS 文件系统上,此锁定机制可能无法正常工作。这是因为 fcntl() 文件锁定在许多 NFS 实现上都已损坏。如果您有多个进程可能同时尝试访问文件,则应避免将 SQLite 数据库文件放在 NFS 上。在 Windows 上,Microsoft 的文档指出,如果您没有运行 Share.exe 守护程序,则 FAT 文件系统下的锁定可能无法正常工作。那些对 Windows 有很多经验的人告诉我,网络文件的锁定非常容易出错且不可靠。如果他们说的是真的,那么在两个或多个 Windows 机器之间共享 SQLite 数据库可能会导致意外问题。

据我们所知,没有其他嵌入式 SQL 数据库引擎支持像 SQLite 这样多的并发性。SQLite 允许多个进程同时打开数据库文件,并允许多个进程同时读取数据库。但是,当任何进程想要写入时,它必须在更新期间锁定整个数据库文件。但这通常只需要几毫秒。其他进程只需等待写入器完成,然后继续执行其操作。其他嵌入式 SQL 数据库引擎通常只允许一个进程连接到数据库。

但是,客户端/服务器数据库引擎(例如 PostgreSQL、MySQL 或 Oracle)通常支持更高水平的并发性,并允许多个进程同时写入同一个数据库。这在客户端/服务器数据库中是可能的,因为始终有一个可用的单一且受良好控制的服务器进程来协调访问。如果您的应用程序需要大量并发性,那么您应该考虑使用客户端/服务器数据库。但经验表明,大多数应用程序需要的并发性远低于其设计者想象的。

当 SQLite 尝试访问被另一个进程锁定的文件时,默认行为是返回 SQLITE_BUSY。您可以使用 sqlite3_busy_handler()sqlite3_busy_timeout() API 函数从 C 代码中调整此行为。

(6) SQLite 是线程安全的吗?

线程是邪恶的。避免使用它们。

SQLite 是线程安全的。我们做出这种让步,因为许多用户选择忽略上一段中给出的建议。但是,为了线程安全,SQLite 必须编译时将 SQLITE_THREADSAFE 预处理器宏设置为 1。发行版中的 Windows 和 Linux 预编译二进制文件都是这样编译的。如果您不确定您正在链接的 SQLite 库是否已编译为线程安全,则可以调用 sqlite3_threadsafe() 接口来了解。

SQLite 是线程安全的,因为它使用互斥锁来序列化对公共数据结构的访问。但是,获取和释放这些互斥锁的工作会稍微降低 SQLite 的速度。因此,如果您不需要 SQLite 是线程安全的,则应禁用互斥锁以获得最大性能。有关更多信息,请参阅 线程模式 文档。

在 Unix 下,您不应将打开的 SQLite 数据库跨 fork() 系统调用传递到子进程中。

(7) 如何列出 SQLite 数据库中包含的所有表/索引

如果您正在运行 sqlite3 命令行访问程序,则可以键入“.tables”以获取所有表的列表。或者,您可以键入“.schema”以查看完整的数据库模式,包括所有表和索引。这两个命令都可以后跟一个 LIKE 模式,该模式将限制显示的表。

在 C/C++ 程序(或使用 Tcl/Ruby/Perl/Python 绑定的脚本)中,您可以通过对名为“SQLITE_SCHEMA”的特殊表执行 SELECT 来访问表和索引名称。每个 SQLite 数据库都有一个 SQLITE_SCHEMA 表,它定义了数据库的模式。SQLITE_SCHEMA 表如下所示

CREATE TABLE sqlite_schema (
  type TEXT,
  name TEXT,
  tbl_name TEXT,
  rootpage INTEGER,
  sql TEXT
);

对于表,type 字段将始终为 'table'name 字段将为表的名称。因此,要获取数据库中所有表的列表,请使用以下 SELECT 命令

SELECT name FROM sqlite_schema
WHERE type='table'
ORDER BY name;

对于索引,type 等于 'index'name 是索引的名称,tbl_name 是索引所属表的名称。对于表和索引,sql 字段是创建表或索引的原始 CREATE TABLE 或 CREATE INDEX 语句的文本。对于自动创建的索引(用于实现 PRIMARY KEY 或 UNIQUE 约束),sql 字段为 NULL。

SQLITE_SCHEMA 表不能使用 UPDATE、INSERT 或 DELETE 进行修改(除非在特殊情况下)。SQLITE_SCHEMA 表会由 CREATE TABLE、CREATE INDEX、DROP TABLE 和 DROP INDEX 等命令自动更新。

临时表不会出现在 SQLITE_SCHEMA 表中。临时表及其索引和触发器存在于另一个名为 SQLITE_TEMP_SCHEMA 的特殊表中。SQLITE_TEMP_SCHEMA 的工作方式与 SQLITE_SCHEMA 完全相同,只是它仅对创建临时表的应用程序可见。要获取所有表(永久表和临时表)的列表,可以使用类似以下的命令

SELECT name FROM 
   (SELECT * FROM sqlite_schema UNION ALL
    SELECT * FROM sqlite_temp_schema)
WHERE type='table'
ORDER BY name

(8) SQLite 数据库是否存在已知的尺寸限制?

有关 SQLite 限制的完整讨论,请参阅limits.html

(9) SQLite 中 VARCHAR 的最大尺寸是多少?

SQLite 不会强制执行 VARCHAR 的长度。您可以声明一个 VARCHAR(10),SQLite 将很乐意在那里存储一个 5 亿字符的字符串。并且它将完整地保留所有 5 亿个字符。您的内容永远不会被截断。SQLite 将“VARCHAR(N)”的列类型理解为与“TEXT”相同,而不管N的值是多少。

(10) SQLite 是否支持 BLOB 类型?

SQLite 允许您在任何列中存储 BLOB 数据,即使是声明为保存其他类型的列。BLOB 甚至可以作为 PRIMARY KEY 使用。

(11) 如何在 SQLite 中向现有表添加、删除或重命名列?

SQLite 的 ALTER TABLE 支持有限,您可以使用它来添加、重命名或删除列,或更改表的名称,如ALTER TABLE 中所述。

如果您想对表的结构或约束或其列进行更复杂的更改,则需要重新创建它。您可以将现有数据保存到临时表中,删除旧表,创建新表,然后从临时表中复制数据。有关过程,请参阅进行其他类型的表架构更改

(12) 我删除了许多数据,但数据库文件没有变小。这是错误吗?

不是。当您从 SQLite 数据库中删除信息时,未使用的磁盘空间将添加到内部的“空闲列表”中,并在下次插入数据时重用。磁盘空间不会丢失。但它也不会返回到操作系统。

如果您删除了许多数据并希望缩小数据库文件,请运行VACUUM命令。VACUUM 将从头开始重建数据库。这将使数据库具有空的空闲列表和大小最小的文件。但是请注意,VACUUM 可能需要一些时间才能运行,并且在运行时它可以使用多达原始文件两倍的临时磁盘空间。

使用 VACUUM 命令的替代方法是自动真空模式,可以使用auto_vacuum pragma启用。

(13) 我可以在我的商业产品中使用 SQLite 而无需支付版税吗?

是的。SQLite 属于公共领域。对代码的任何部分均不提出所有权声明。您可以随心所欲地使用它。

(14) 如何使用包含嵌入式单引号 (') 字符的字符串文字?

SQL 标准规定,字符串中的单引号通过连续放置两个单引号来转义。在这方面,SQL 的工作方式类似于 Pascal 编程语言。示例

    INSERT INTO xyz VALUES('5 O''clock');
  

(15) 什么是 SQLITE_SCHEMA 错误,为什么我会遇到此错误?

当准备好的 SQL 语句不再有效且无法执行时,将返回SQLITE_SCHEMA错误。发生这种情况时,必须使用sqlite3_prepare()API 从 SQL 重新编译语句。SQLITE_SCHEMA错误只能在使用sqlite3_prepare()sqlite3_step()接口运行 SQL 时发生。您永远不会从sqlite3_exec()收到SQLITE_SCHEMA错误。如果您使用sqlite3_prepare_v2()而不是sqlite3_prepare()准备语句,也不会收到错误。

sqlite3_prepare_v2()接口创建了一个准备好的语句,如果模式发生更改,它将自动重新编译自身。处理SQLITE_SCHEMA错误的最简单方法是始终使用sqlite3_prepare_v2()而不是sqlite3_prepare()

(17) 编译 SQLite 时,我收到一些编译器警告。这不是问题吗?它是否表明代码质量差?

SQLite 中的质量保证是使用全覆盖测试完成的,而不是通过编译器警告或其他静态代码分析工具完成的。换句话说,我们验证 SQLite 是否确实获得了正确的答案,而不是它仅仅满足风格约束。SQLite 代码库的大部分内容纯粹用于测试。SQLite 测试套件运行数万个独立的测试用例,其中许多测试用例是参数化的,因此在每次发布之前都会运行和评估数亿个涉及数十亿个 SQL 语句的测试用例的正确性。开发人员使用代码覆盖率工具来验证代码中的所有路径都经过了测试。每当在 SQLite 中发现错误时,都会编写新的测试用例来展示该错误,以防止将来该错误未被检测到而再次发生。

在测试期间,SQLite 库使用特殊的检测工具进行编译,该工具允许测试脚本模拟各种故障,以验证 SQLite 是否能够正确恢复。内存分配会小心地跟踪,并且即使在内存分配失败后也不会发生内存泄漏。自定义 VFS 层用于模拟操作系统崩溃和断电,以确保事务在这些事件中是原子的。故意注入 I/O 错误的机制表明 SQLite 对此类故障具有弹性。(作为一个实验,尝试在其他 SQL 数据库引擎上诱发此类错误,看看会发生什么!)

我们还在 Linux 上使用Valgrind运行 SQLite,并验证它没有检测到任何问题。

有些人说我们应该消除所有警告,因为良性的警告会掩盖将来更改中可能出现的真实警告。这确实足够正确。但作为回应,开发人员观察到,在用于 SQLite 开发的构建中(GCC、MSVC 和 clang 的各种版本),所有警告都已修复。编译器警告通常仅来自 SQLite 开发人员自己不使用的编译器或编译时选项。

(18) Unicode 字符的不区分大小写匹配不起作用。

SQLite 的默认配置仅支持 ASCII 字符的不区分大小写比较。这样做的原因是,执行完整的 Unicode 不区分大小写比较和大小写转换需要表和逻辑,这将使 SQLite 库的大小几乎增加一倍。SQLite 开发人员认为,任何需要完整 Unicode 大小写支持的应用程序可能已经拥有必要的表和函数,因此 SQLite 不应该占用空间来复制此功能。

SQLite 没有默认提供完整的 Unicode 大小写支持,而是提供了链接到外部 Unicode 比较和转换例程的功能。应用程序可以重载内置的NOCASE排序规则(使用sqlite3_create_collation())以及内置的like()upper()lower()函数(使用sqlite3_create_function())。SQLite 源代码包含执行这些重载的“ICU”扩展。或者,开发人员可以根据其项目中已包含的自己的 Unicode 感知比较例程编写自己的重载。

(19) INSERT 速度非常慢 - 我每秒只能执行几十个 INSERT

实际上,SQLite 在普通台式计算机上可以轻松地每秒执行 50,000 个或更多INSERT语句。但它每秒只能执行几十个事务。事务速度受磁盘驱动器的旋转速度限制。事务通常需要磁盘盘片完全旋转两次,在 7200RPM 磁盘驱动器上,这将您限制为每秒大约 60 个事务。

事务速度受磁盘驱动器速度限制,因为(默认情况下)SQLite 实际上会等到数据真正安全地存储在磁盘表面上,事务才会完成。这样,如果您突然断电或操作系统崩溃,您的数据仍然安全。有关详细信息,请阅读有关SQLite 中的原子提交的信息。

默认情况下,每个 INSERT 语句都是一个事务。但是,如果您用BEGIN...COMMIT将多个 INSERT 语句括起来,则所有插入都将分组到一个事务中。提交事务所需的时间在所有包含的插入语句中分摊,因此每个插入语句的时间大大减少。

另一种选择是运行PRAGMA synchronous=OFF。此命令将导致 SQLite 不等待数据到达磁盘表面,这将使写入操作看起来快得多。但是,如果您在事务过程中断电,则数据库文件可能会损坏。

(20) 我不小心从 SQLite 数据库中删除了一些重要信息。如何恢复它?

如果您有数据库文件的备份副本,请从备份中恢复信息。

如果您没有备份,则恢复非常困难。您可能能够在原始数据库文件的二进制转储中找到部分字符串数据。鉴于特殊的工具,恢复数值数据也可能是可能的,尽管据我们所知,此类工具不存在。SQLite 有时会使用SQLITE_SECURE_DELETE选项进行编译,该选项会将所有已删除的内容用零覆盖。如果是这种情况,则恢复显然是不可能的。如果您在数据删除后运行了VACUUM,则恢复也是不可能的。如果未使用 SQLITE_SECURE_DELETE 且未运行 VACUUM,则某些已删除的内容可能仍位于数据库文件中,位于标记为可重用区域中。但是,同样,我们不知道有任何程序或工具可以帮助您恢复这些数据。

(21) 什么是 SQLITE_CORRUPT 错误?数据库“格式错误”意味着什么?为什么我会遇到此错误?

当 SQLite 检测到数据库文件结构、格式或其他控制元素中的错误时,将返回SQLITE_CORRUPT错误。

SQLite 不会在没有外部帮助的情况下损坏数据库文件。如果您的应用程序在更新过程中崩溃,您的数据是安全的。即使操作系统崩溃或断电,数据库也是安全的。SQLite 的抗崩溃性已得到广泛的研究和测试,并且得到了数十亿用户多年真实世界经验的证明。

也就是说,外部程序或硬件或操作系统中的错误可以做一些事情来损坏数据库文件。有关更多信息,请参阅如何损坏 SQLite 数据库文件

您可以使用PRAGMA integrity_check对数据库完整性进行彻底但耗时的测试。

您可以使用PRAGMA quick_check对数据库完整性进行更快但不太彻底的测试。

根据数据库损坏的严重程度,您可以使用 CLI 将模式和内容转储到文件中,然后重新创建,从而恢复部分数据。不幸的是,一旦 Humpty Dumpty 从墙上掉下来,通常就不可能再把他拼凑起来了。

(22) SQLite 是否支持外键?

3.6.19 版(2009-10-14)开始,SQLite 支持外键约束。但是,默认情况下会关闭外键约束的强制执行(为了向后兼容)。要启用外键约束强制执行,请运行PRAGMA foreign_keys=ON或使用-DSQLITE_DEFAULT_FOREIGN_KEYS=1进行编译。

(23) 如果在构建 SQLite 时使用 SQLITE_OMIT_... 编译时选项,我会收到编译器错误。

SQLITE_OMIT_...编译时选项仅在从规范源文件构建时有效。当您从 SQLite合并或从预处理的源文件构建时,它们无效

可以构建一个特殊的合并文件,使其与预定的 SQLITE_OMIT_... 选项一起工作。有关操作说明,请参阅SQLITE_OMIT_... 文档

(24) 我的 WHERE 子句表达式column1="column1"不起作用。它会导致返回表的每一行,而不仅仅是 column1 值为 "column1" 的行。

在 SQL 中,使用单引号而不是双引号括起字符串文字。这是 SQL 标准的要求。您的 WHERE 子句表达式应为column1='column1'

SQL 使用双引号括起包含特殊字符或为关键字的标识符(列名或表名)。因此,双引号是转义标识符名称的一种方法。因此,当您说column1="column1"这等价于column1=column1这显然总是为真。

(25) SQLite 的语法图(也称为“铁路”图)是如何生成的?

每个图都是使用Pikchr 绘图语言手工编写的。这些手写规范被转换为 SVG,并在文档构建过程中内嵌到 HTML 文件中。

SQLite 文档的许多历史版本使用不同的过程生成语法图。历史过程基于 Tcl/Tk,并在http://wiki.tcl-lang.org/21708 中进行了描述。新的基于 Pikchr 的语法图于 2020 年 9 月 26 日首次进入主干。

(26) SQL 标准要求即使唯一约束中的一个或多个列为 NULL,也必须强制执行唯一约束,但 SQLite 不会这样做。这不是一个错误吗?

您可能指的是 SQL92 中的以下语句
当且仅当表中没有两行在唯一列中具有相同的非空值时,唯一约束才满足。
该语句模棱两可,至少有两种可能的解释
  1. 当且仅当表中没有两行具有相同的值并在唯一列中具有非空值时,唯一约束才满足。
  2. 当且仅当表中没有两行在非空唯一列的子集中具有相同的值时,唯一约束才满足。
SQLite 遵循解释 (1),PostgreSQL、MySQL、Oracle 和 Firebird 也遵循此解释。确实,Informix 和 Microsoft SQL Server 使用解释 (2),但是我们 SQLite 开发人员认为解释 (1) 是对需求的最自然解读,我们也希望最大程度地与其他 SQL 数据库引擎兼容,而且大多数其他数据库引擎也使用 (1),所以 SQLite 就是这么做的。

(27) SQLite 的出口管制分类号 (ECCN) 是什么?

在仔细审查了《商业管制清单》(CCL) 之后,我们确信核心公共领域的 SQLite 源代码不受任何 ECCN 描述,因此 ECCN 应报告为EAR99

以上内容适用于核心公共领域的 SQLite。如果您通过添加新代码扩展 SQLite,或者如果您将 SQLite 静态链接到您的应用程序,那么这可能会在您的特定情况下更改 ECCN。

(28) 我的查询没有返回我期望的列名。这是一个错误吗?

如果结果集的列由 AS 子句命名,则 SQLite 保证使用 AS 关键字右侧的标识符作为列名。如果结果集未使用 AS 子句,则 SQLite 可以自由命名任何列。有关更多信息,请参阅sqlite3_column_name() 文档。

(29) 我的数据库去哪里了?(或者:我的数据库如何变得空了?)

除非以阻止创建的标志打开,否则如果 SQLite 数据库不存在,则会创建它。新创建的数据库最初为空。这可能会让那些由于文件名中的拼写错误或使用与打开进程的当前目录不同的相对路径名而在不同上下文中无意中打开不同数据库文件的人感到困惑。

此页面上次修改于2023-12-22 14:38:37 UTC