(1) 如何创建一个自增(AutoIncrement)字段?
声明一个列为 INTEGER PRIMARY KEY,无论何时插入一个NULL到该列,NULL会自动转换为比目前所有行所记录的数值更大的数,如果表中没有记录该值被赋为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() 函数会返回最近的insert操作的整型值
注意,整型键比插入前表中最大的键大1。 新插入的键值一定是表中唯一的,但是它有可能被赋值成之前被删除的行所设的值。若是想让这个值永远在表中都是唯一的,那么需要添加AUTOINCREMENT 关键字。这样新值会一直比表中最大的值 大1.如果值已经到达了最大限制,那么这个插入操作将会失败并提示SQLITE_FULL 错误码。
(2)SqLite支持的数据类型有哪些?
SQLite使用动态类型 dynamic typing,数据可以被存储为,INTEGER, REAL, TEXT, BLOB, NULL.
(3) SQLite允许插入一个字符串String到声明为Integer的列中
这是数据库的特性,并不是一个bug。SQLite使用动态类型。它对数据类型的约束不做强制限制。任意类型的数据通常可以被插入到任意列中。你可以将任意长度的字符串插入到整型列中,浮点数到布尔类型的列中,或是将日期插入到字符列中。在创建表的命令中指定列的数据类型并没有限制什么类型可以被插入到列中。每个列都可以储存任意长度的字符串(有一个例外是:被定义为INTEGER PRIMARY KEY 的列只能储存64位有符号整型。如果你尝试将有别于integer的其他数据存入该列中将会报错。)
但是SQLite的确使用声明类型来定义列,作为一种提示让你更应该用特定的数据去填充它。举例来说,如果一个列被定义为整型那么你试图插入一个字符串的话,SQLite将视图把它转换成为一个整型。如果可以的话,它将插入一个整型来取代原值。如果不可以的话,再插入字符串。这个特性叫做类型亲和性dtype affinity。
(4) 为什么SQLite不允许在同一个表中的两个不同行中使用‘0’和‘0.0’分别作为主键?
这个问题会发生在你将主键定义为数值类型时。若想避免这个问题,将主键的数据类型换成 TEXT即可。
每行必须有一个唯一的主键。对于一个数值类型的列,SQLite认为‘0’和‘0.0’是相同的值,这是因为在进行数值类型比较的时候两者相较是相等的。因此这些值并不唯一。
(5) 多个进程或同一进程多个数据库实例可以在同一时间访问同一数据库文件?
多个进程可以同时打开同一数据库。多进程可以同时进行查询操作。但是,在任何时候只有一个进程可以对数据库进行更改。
SQLite使用读写锁来控制数据库的访问。使用时需要注意:如果数据库文件是存放在一个NFS文件系统中,锁机制可能不会正常工作。这是因为 fcntl()文件锁在许多NFS实现中被破坏。在Windows系统中,微软的文档中提及,如果没有运行share.exe守护进程,锁可能在FAT文件系统中不能工作。
目前没有其他SQL数据库引擎像SQLite这样支持如此之多的并发。SQLite允许多个进程同一时间打开同一个数据库文件,并进行读操作。当任意一个进程想要进行写,它必须对整个数据库文件在修改期间进行加锁。但是通常这种操作只需要几毫秒即可完成 。其他的进程等待写入完成便可继续执行。其他的嵌入式SQL数据库引擎一般只允许一个进程连接一个数据库。
然而,服务器上的数据库引擎(例如PostgreSQL,MySQL,Oracle)通常支持更高级别的并发并允许多进程同时对同一数据库进行写入操作。这只在服务器上的数据库上可行,这是因为通常有一个单一的可控的服务进程去协调数据库的访问。如果你的应用有很多的并发需求,那么你应该考虑使用服务器上用的数据库。但从使用经验来看大多数的应用并没有设计者想象的需要那么多的并发。
当SQLite试图访问一个被另一进程锁住的文件,默认的行为是返回SQLITE_BUSY。你可以调整这个行为通过改写sqlite3_busy_handler() 或 sqlite3_busy_timeout() 函数来实现.
(6) SQLite是线程安全的吗?
SQLite是线程安全的。我们之所以做出这样的让步,是因为许多用户选择忽略前一段给出的建议。 但是为了线程安全,SQLite编译时必须将SQLITE_THREADSAFE预处理器宏设置为1。 发行版中的Windows和Linux预编译二进制文件都是用这种方式编译的。 如果你不确定你链接的SQLite库是否被编译为线程安全的,你可以调用sqlite3_threadsafe()接口来找出答案。
SQLite是线程安全的,因为它使用互斥锁来序列化对公共数据结构的访问。 然而,获取和释放这些互斥锁的工作将略微降低SQLite的速度。 因此,如果你不需要SQLite是线程安全的,您应该禁用互斥锁以获得最大的性能。 有关更多信息,请参阅threading mode文档。在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 );
对于表,类型字段总是'table',而name字段将是表的名称。 因此,要获取数据库中所有表的列表,使用下面的SELECT命令:
SELECT name FROM sqlite_schema WHERE type='table' ORDER BY name;
对于索引,类型等于'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数据有哪些已知的大小限制?
详见 limits.html ,其中列出了所有的限制
(9) SQLite中VARCHAR的最大长度是多少
SQLite并没有限制VARCHAR的长度。你可以定义VARCHAR(10),SQLite将很乐意在那里存储一个5亿字符的字符串。并完整的保存这些字符。你的内容不会被截断。SQLite会把“VARCHAR(N)”当做“TEXT”处理,不论这个N是多少。
(10) SQLite支持BLOB类型吗?
在SQLite中任意列都可以保存BLOB数据,即便列已经被声明了其他的类型。BLOB甚至可以用在主键列上。
(11)如何在SQLite已存在的表中进行添加、删除或重命名列?
SQLite对ALTER TABLE的支持有限,你可以使用它来添加、重命名或删除列或更改表名。
如果你想对表及其列的结构或约束进行更复杂的更改, 那就必须要重新创建它了。你可以把数据存到一个临时表中,然后删除旧表,再将临时表的数据拷贝回来。详见: Making Other Kinds Of Table Schema Changes
(12) 删除了很多数据但是数据库文件体积并没有减小,这是否是个Bug?
不是bug。当删除数据库中的信息,未使用的磁盘空间被添加到了“free-list”中并在下次插入数据时候进行重用。磁盘空间没有丢失。但也没有返还给操作系统。
如果你删除了很多数据并想减少数据库文件的体积,执行VACUUM 命令。VACUUM 将从 scratch重建数据库。这将使数据库拥有一个空的free-list并且得到一个最小的体积。注意,VACUUM 将花费一些时间运行,它在运行时使用的临时磁盘空间最多可以是原始文件的两倍。
使用VACUUM命令的另一种选择是使用auto_vacuum pragmal来启用自动清空模式。
(13) 如何使用内部包含单引号(')字符的字符串字面值?
SQL标准指定,通过在一行中放入两个单引号来转义字符串中的单引号。 在这方面,SQL像Pascal语言一样。举个例子:
INSERT INTO xyz VALUES('5 O''clock');
(14) INSERT速度慢,每秒只能执行几十次INSERT操作
实际上,SQLite在台式机上每秒可以轻松执行5万条或更多的INSERT语句。 但是每秒只能处理几十次事务。事务处理速度受磁盘驱动器的转速限制。 一个事务通常需要磁盘的两次完整旋转,这在一个7200RPM磁盘驱动器上限制是每秒大约60个事务。
事务处理速度受磁盘驱动器速度的限制,因为(默认情况下)SQLite实际上要等到数据真正安全地存储在磁盘上才完成事务处理。 这样,如果你突然断电或操作系统崩溃,你的数据仍然是安全的。 有关详细信息,请阅读atomic commit in SQLite.。
默认情况下,每个INSERT语句既是自身的事务。 但是如果使用BEGIN…COMMIT包围多个INSERT语句,那么所有的插入都将被分组到单个事务中。 提交事务所需的时间摊到所有附带的插入语句上,因此每个插入语句的时间大大减少。
另一个选项是运行PRAGMA synchronous=OFF。 该命令将导致SQLite不等待数据到达磁盘表面,这将使写操作看起来快得多。 但是,如果在事务处理过程中断点,数据库文件可能会损坏。
(15)不小心删除了SQLite数据库中的重要信息,如何恢复它?
如果你有数据库文件的备份,从备份中恢复信息。
如果没有备份,恢复是非常困难的。 你或许能够在原始数据库文件的二进制转储中找到部分字符串数据。 如果有特殊的工具,恢复数值数据也是可能的,尽管据我们所知没有这样的工具存在。 SQLite有时使用SQLITE_SECURE_DELETE选项编译,该选项会用零来重写所有已删除的内容。 如果是这样的话,那么复苏显然是不可能的。 如果在删除数据后运行VACUUM,则恢复也是不可能的。 如果没有使用SQLITE_SECURE_DELETE,也没有运行VACUUM,那么一些被删除的内容可能仍然在数据库文件中,位于标记为可重用的区域。 但是,我们也不知道有什么程序或工具可以帮助你恢复这些数据。
(16)什么是SQLite Corrupt错误?数据库Malformed意味着什么,以及这个错误的原因
当SQLite检测到数据库文件的结构、格式或其他控制元素中的错误时,返回一个SQLITE_CORRUPT错误。
SQLite不会在没有外部力量介入的的情况下主动去损坏数据库文件。 如果您的应用程序在更新过程中崩溃,那么您的数据是安全的。 即使操作系统崩溃或断电,数据库也是安全的。 SQLite的抗崩溃性已经得到了广泛的研究和测试,并得到了数十亿用户多年的实际经验的证明。
也就是说,你的硬件或操作系统中的外部程序或bug可能会破坏数据库文件。 参见如何破坏SQLite数据库文件了解更多信息。
你可以使用PRAGMA integrity_check对数据库完整性进行彻底但耗时的测试。
可以使用PRAGMA quick_check对数据库完整性进行更快但不太彻底的测试。
(17) SQLite支持外键吗?
自版本version 3.6.19 (2009-10-14)起,SQLite支持外键约束foreign key constraints.但是外键约束的强制执行在默认情况下是关闭的(为了向后兼容) 。通过执行 PRAGMA foreign_keys=ON来启用。
(18) My WHERE clause expression column1="column1"
does not work. It causes every row of the table to be returned, not just the rows where column1 has the value "column1".
标准SQL的要求便是使用单引号,你的条件字句应该写成:
column1='column1'
SQL在包含特殊字符或关键字的标识符(列或表名)周围使用双引号。双引号是对标识符名称进行转义的一种方式 。因此,当你写
column1="column1"
其实是和column1=column1
意思等同的,很显然结果永远是true。
**(19) SQL标准要求强制执行UNIQUE唯一性约束,即使约束中的一个或多个列为NULL,但SQLite不这样做。 那不是一个bug吗 **
也许你引用的是SQL92中的以下说明:
当且仅当表中没有两行在唯一列中具有相同的非空值时,才满足唯一约束
这一说法模棱两可,至少有两种可能的解释 :
- 当且仅当表中没有两行具有相同的值且唯一列中有非空值时,才满足唯一约束。
- 当且仅当表中没有两行在唯一列的子集中具有不为空的相同值时,才满足惟一约束。
SQLite遵循解释(1),PostgreSQL, MySQL, Oracle和Firebird也是如此。 Informix和Microsoft SQL Server却是使用解释(2),但是我们SQLite开发人员认为解释(1)是对需求最自然的解读,我们还希望能与其他SQL数据库引擎能最大化地的兼容,大多数其他数据库引擎也使用解释(1),所以这就是SQLite所做的。
(20) 查询没有返回期望的列名,这是否是个bug?
如果结果集的列是由AS子句命名的,那么SQLite保证使用AS关键字右侧的标识符作为列名。 如果结果集不使用AS子句,那么SQLite可以随意为列命名。 有关更多信息,请参阅sqlite3_column_name()文档。