Small. Fast. Reliable.
Choose any three.

经常问的问题

  1. 如何创建AUTOINCREMENT字段?
  2. SQLite支持哪些数据类型?
  3. SQLite让我在整数类型的数据库列中插入一个字符串!
  4. 为什么SQLite不允许我在同一表的两个不同行上使用“ 0”和“ 0.0”作为主键?
  5. 多个应用程序或同一应用程序的多个实例可以同时访问一个数据库文件吗?
  6. SQLite线程安全吗?
  7. 如何列出SQLite数据库中包含的所有表/索引
  8. SQLite数据库是否存在已知的大小限制?
  9. SQLite中VARCHAR的最大大小是多少?
  10. SQLite是否支持BLOB类型?
  11. 如何在SQLite中的现有表中添加或删除列。
  12. 我删除了很多数据,但是数据库文件没有变小。这是一个错误吗?
  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,也必须强制执行UNIQUE约束,但是SQLite不会这样做。那不是错误吗?
  26. 什么是SQLite的导出控制分类号(ECCN)?
  27. 我的查询未返回我期望的列名。这是一个错误吗?

(1)如何创建一个AUTOINCREMENT字段?

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

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

创建表t1(
  整数主键,
  b整数
);

有了这个表,语句

插入t1值(NULL,123);

从逻辑上讲等同于说:

插入t1值((从t1中选择max(a))+ 1,123);

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

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

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

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

(3)SQLite让我在整数类型的数据库列中插入一个字符串!

这是一个功能,而不是错误。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经验的人告诉我,网络文件的文件锁定非常容易出错,并且不可靠。如果他们说的是真的,

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

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

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

(6)SQLite线程安全吗?

线程是邪恶的。避免他们。

SQLite是线程安全的。我们做出这个让步,因为许多用户选择忽略上一段中给出的建议。但是,为了确保线程安全,必须在将SQLITE_THREADSAFE预处理器宏设置为1的情况下编译SQLite。如果不确定要链接的SQLite库是否编译为线程安全的,则可以调用sqlite3_threadsafe() 接口进行查找。

SQLite是线程安全的,因为它使用互斥锁来序列化对常见数据结构的访问。但是,获取和释放这些互斥锁的工作会使SQLite的运行速度稍慢一些。因此,如果您不需要SQLite具有线程安全性,则应禁用互斥锁以实现最佳性能。有关其他信息,请参见线程模式文档。

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

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

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

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

创建表sqlite_schema(
  输入TEXT,
  名称TEXT,
  tbl_name TEXT,
  rootpage INTEGER,
  sql文本
);

对于表,类型字段将始终为“表”名称字段将为表的名称。因此,要获取数据库中所有表的列表,请使用以下SELECT命令:

从sqlite_schema中选择名称
WHERE type ='table'
ORDER BY名称;

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

无法使用UPDATE,INSERT或DELETE修改SQLITE_SCHEMA表(在特殊情况下除外 )。SQLITE_SCHEMA表由诸如CREATE TABLE,CREATE INDEX,DROP TABLE和DROP INDEX之类的命令自动更新。

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

选择名称自 
   (选择*来自sqlite_schema联合全部
    SELECT * FROM sqlite_temp_schema)
WHERE type ='table'
按名称订购

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

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

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

SQLite不强制VARCHAR的长度。您可以声明VARCHAR(10),SQLite乐于在此存储一个5亿个字符串。它将保留所有5亿个字符的完整性。您的内容永远不会被截断。无论N的值如何,SQLite都将“ VARCHAR(N)”的列类型与“ TEXT”相同。

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

SQLite允许您将BLOB数据存储在任何列中,即使声明为容纳其他类型的列也是如此。BLOB甚至可以用作主键。

(11)如何在SQLite中的现有表中添加或删除列。

SQLite具有有限的 ALTER TABLE支持,可用于将列添加到表的末尾或更改表的名称。如果要对表的结构进行更复杂的更改,则必须重新创建表。您可以将现有数据保存到临时表中,删除旧表,创建新表,然后将数据从临时表中复制回去。

例如,假设您有一个名为“ t1”的表,其列名为“ a”,“ b”和“ c”,并且您想从该表中删除列“ c”。以下步骤说明了如何完成此操作:

开始交易;
创建临时表t1_backup(a,b);
INSERT INTO t1_backup从t1选择a,b;
拖放表t1;
创建表t1(a,b);
从t1_backup插入t1 SELECT a,b;
删除表t1_backup;
犯罪;

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

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

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

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

(13)我可以在不支付特许权使用费的情况下在我的商业产品中使用SQLite吗?

是的。SQLite在 公共领域。不对代码的任何部分主张所有权。您可以用它做任何您想做的事。

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

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

    插入xyz值('5 O'clock');
  

(15)什么是SQLITE_SCHEMA错误,为什么我得到一个错误?

一个SQLITE_SCHEMA当准备SQL语句不再有效,并且不能被执行则返回错误。发生这种情况时,必须使用sqlite3_prepare() API从SQL重新编译该语句。仅当使用sqlite3_prepare()sqlite3_step()接口运行SQL时,才会发生SQLITE_SCHEMA错误。您将永远不会从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_collat​​ion())和内置的 like()upper()lower()函数(使用sqlite3_create_function())。SQLite源代码包括执行这些重载的“ ICU”扩展。或者,开发人员可以根据项目中已经包含的自己的Unicode感知比较例程编写自己的重载。

(19)INSERT真的很慢-我每秒只能做几十个INSERT

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

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

默认情况下,每个INSERT语句都是其自己的事务。但是,如果用BEGIN ... COMMIT包围多个INSERT语句,则所有插入都被分组为一个事务。提交事务所需的时间在所有随附的插入语句中摊销,因此每个插入语句的时间大大减少。

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

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

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

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

(21)什么是SQLITE_CORRUPT错误?数据库“格式错误”是什么意思?为什么会出现此错误?

一个SQLITE_CORRUPT当SQLite的在结构中,格式或数据库文件的其它控制元件检测到错误,则返回错误。

没有外部帮助,SQLite不会破坏数据库文件。如果您的应用程序在更新过程中崩溃,则您的数据是安全的。即使您的操作系统崩溃或断电,该数据库也是安全的。SQLite的防撞性已被广泛研究和测试,数十亿用户的多年实际经验证明了这一点。

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

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

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

根据数据库损坏的严重程度,您可以使用CLI将架构和内容转储到文件然后重新创建,从而能够恢复某些数据。不幸的是,一旦矮胖从墙上掉下来,通常就不可能再将他重新放在一起。

(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-09-26首次登陆树干。

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

也许您是指SQL92中的以下语句:
当且仅当表中没有两行在唯一列中具有相同的非空值时,才满足唯一约束。
该声明含糊不清,至少有两种可能的解释:
  1. 当且仅当表中没有两行具有相同的值且唯一列中具有非空值时,才满足唯一约束。
  2. 当且仅当表中没有两行在不为null的唯一列的子集中具有相同的值时,才满足唯一约束。
SQLite遵循解释(1),PostgreSQL,MySQL,Oracle和Firebird也是如此。Informix和Microsoft SQL Server确实使用了解释(2),但是我们SQLite开发人员认为解释(1)是对该要求的最自然的理解,我们还希望最大程度地与其他SQL数据库引擎以及大多数其他数据库引擎兼容。数据库引擎也与(1)一起使用,因此SQLite就是这样做的。

(27)什么是SQLite的导出控制分类号(ECCN)?

在仔细检查了商务控制列表(CCL)之后,我们确信任何ECCN都未描述核心的公共域SQLite源代码,因此ECCN应报告为EAR99

上面对于核心公共域SQLite而言是正确的。如果通过添加新代码扩展SQLite,或者将SQLite与应用程序静态链接,则在特定情况下可能会更改ECCN。

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

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