1.如何创建自增字段?2.SQLite支持哪些数据类型?3.为什么我可以在SQLite数据库的整数字段中插入一个字符串?4.为什么SQLite认为表达式'0'=='00'为真?5.为什么SQLite不允许'0'和'0.0'作为同一张表中两个不同行的主键?6、为什么在SparcStation中创建的SQLite数据库在Linuxbox中无法读取?7.多个应用程序或同一个应用程序的多个例程是否可以同时访问同一个数据库文件?8.SQLite是线程安全的吗?9.如何列出SQLite数据库中的所有表/索引?10.SQLite数据库是否有已知的大小限制?11.SQLite中VARCHAR的最大长度是多少?12.SQLite是否支持BLOB类型?13.如何从现有的SQLite表中添加/删除字段?14.我删除了很多数据但是数据库文件没有减少,是不是bug?15.我可以在不支付版税的情况下将SQLite用于商业用途吗?16.如何使用包含单引号(')的字符串?17.SQLITE_SCHEMA错误是什么意思?18.为什么ROUND(9.95,1)返回9.9而不是10.0?9.95不应该四舍五入吗?(1)如何创建自增字段?简单答案:声明为INTEGERPRIMARYKEY的字段将自动递增。下面是详细的答案:从SQLite2.3.4版本开始,如果你在表中声明一个字段为INTEGERPRIMARYKEY,每当你在该表中的那个字段中插入一个NULL值时,NULL值将被自动替换为比表中该字段所有行的最大值大1的整数;如果表为空,它将被替换为1。例如,假设您有一个这样的数据表:CREATETABLEt1(aINTEGERPRIMARYKEY,bINTEGER);在这个数据表中,语句INSERTINTOt1values(NULL,123);逻辑上等同于:INSERTINTOt1valueS((SELECTmax(a)FROMt1)+1,123);一个新的API函数sqlite3_last_insert_rowid()返回最近插入操作的整数键注意这个整数键总是大于Big1之前插入表中的最后一个键。新键相对于表中现有键是唯一的,但它可能与之前从表中删除的键值重叠。要始终获得整个表中的唯一键,请在声明INTEGERPRIMARYKEY之前添加关键字AUTOINCREMENT。所选键将始终比表中已存在的最大键大1。如果表中已经存在可能的最大键,则INSERT操作将失败并返回SQLITE_FULL错误代码。(2)SQLite支持哪些数据类型?请参阅http://www.sqlite.org/datatype3.html。(3)为什么我可以在SQLite数据库中的整数字段中插入一个字符串?这是一个功能,而不是错误。您可以在任何字段中放置任何信息,无论该字段声明为什么类型。您可以将任意长度的字符串插入整数字段,将浮点数插入布尔字段,或将日期插入字符字段。您在CREATETABLE命令中为该列指定的数据类型不限制插入到该列中的数据。所有字段都可以插入任意长度的字符串。INTEGERPRIMARYKEY字段除外。该字段只能存储64位整数,否则会出错。但是SQLite会假定您要使用声明的字段类型。因此,例如,如果您希望将字符串插入到声明为INTEGER的字段中,SQLite将尝试将其转换为整数。如果转换成功,则插入整数,否则插入字符串,此功能有时称为类型或列亲和性。(4)为什么SQLite认为表达式'0'=='00'为真?2.7.0之后,该表达式不成立。请参阅有关SQLite版本3中数据类型的文档(5)为什么SQLite不允许将“0”和“0.0”作为同一表中两个不同行的主键?你的主键必须是数字类型,把类型改成TEXT就可以了。每行必须有一个唯一的主键。作为一个数字字段,SQLite认为值“0”和“0.0”是相同的,因为它们在数值上比较相等(见上一个问题)因此值不是唯一的。(6)为什么在SparcStation中创建的SQLite数据库无法在Linuxbox中读取?您需要将SQLite库升级到2.6.3或更新版本。x86处理器是小端,而Sparc是大端。较新版本的SQLite解决了这个问题。注意: bigendian和littleendian是CPU处理多字节数的不同方式。例如,字符“汉”的Unicode编码为6C49。那么写文件的时候,是应该把6C写在前面呢,还是应该把49写在前面呢?如果6C写在前面,就是bigendian。或者在前面写49,也就是littleendian。(7)多个应用程序或同一个应用程序的多个例程是否可以同时访问同一个数据库文件?多个进程可以同时打开同一个数据库,也可以同时SELECT。但是只有一个进程可以立即更改数据库。SQLite使用读/写锁来控制数据库访问。(Win95/98/ME操作系统缺少读/写锁支持,在低于2.7.0的版本中,这意味着在windows下一次只能有一个进程读取数据库。在2.7.0版本中解决了这个问题在windows界面代码中实现用户间隔概率读写锁定策略。)但是如果数据库文件在NFS文件系统上,控制并发读取的锁定机制可能会出错。因为NFS的fcntl()文件锁定有时会出现问题。如果有多个进程可能并发读取数据库,则需要避免将数据库文件放在NFS文件系统中。根据Microsoft的文档,如果没有运行Share.exe守护程序,锁定FAT文件系统可能无法工作。对Windows非常有经验的人告诉我,网络文件锁定存在错误且不可靠。如果是这样,在2个或多个Windows系统之间共享单个SQLite数据库文件可能会导致不可预测的问题。我们知道没有其他嵌入式SQL数据库引擎比SQLite支持更多的并发性。SQLite允许多个进程同时打开和读取数据库。当任何一个进程需要写入时,整个数据库将在进程中被锁定。但这通常只需要几毫秒。其他进程只是等待并继续其他事务。其他嵌入式SQL数据库引擎往往只允许单个进程访问数据库。但是客户端/服务器数据库引擎(如PostgreSQL、MySQL、Oracle)通常支持更高的并发,支持多个进程同时写入同一个数据库。这是有保证的,因为总是有一个控制良好的服务器协调数据库访问。如果您的应用程序需要高并发性,您应该考虑使用客户端/服务器数据库。事实上,经验告诉我们,大多数应用程序需要的并发性远低于其设计者的想象。默认行为是当SQLite试图操作被另一个进程锁定的文件时返回SQLITE_BUSY。您可以使用C代码更改此行为。使用sqlite3_busy_handler()或sqlite3_busy_timeout()API函数。如果两个或多个进程同时打开同一个数据库,并且其中一个进程创建了新表或索引,则其他进程可能无法立即看到新表。其他进程可能需要关闭并重新连接到数据库。(8)SQLite是线程安全的吗?有时候是。SQLite要线程安全,必须在THREADSAFE预处理宏设置为1的情况下编译。Windows版本在默认分发编译版本中是线程安全的,但Linux版本不是。如果需要线程安全,需要重新编译linux版本。“线程安全”是指两个或三个线程可以同时独立调用sqlite3_open()返回的不同“sqlite3”结构。而不是同时在多个线程中使用相同的sqlite3结构指针。sqlite3结构只能由调用sqlite3_open创建它的进程使用。不能在一个线程中打开一个数据库,然后将指针传递给另一个线程使用。这是由于大多数多线程系统(如RedHat9)的限制(或错误?)。在这些有问题的系统上,一个线程创建的fcntl()锁不能被另一个线程删除或修改。由于SQLite依赖于fcntl()锁来进行并发控制,因此在线程之间传递数据库连接时会出现严重的问题。在Linux下可能有解决fcntl()锁定问题的方法,但那将非常复杂并且极难测试其正确性。因此,SQLite目前不允许在线程之间共享句柄。在UNIX下,您不能将打开的SQLite数据库放入带有fork()系统调用的子例程中,否则会出错。(9)如何列出SQLite数据库中的所有表/索引?在sqlite3命令行程序中,您可以使用命令“.tables”来显示所有表或使用“.schema”来显示所有表结构和索引。但不要在命令后跟LIKE语句,否则会限制表的显示。在C/C++程序(或使用Tcl/Ruby/Perl/Python绑定的脚本)中,您可以通过访问名为“SQLITE_MASTER”的表来执行此操作。每个SQLite数据库都有一个SQLITE_MASTER表,里面包含了数据库的结构SQLITE_MASTER表是这样的:CREATETABLEsqlite_master(typeTEXT,nameTEXT,tbl_nameTEXT,rootpageINTEGER,sqlTEXT);对于表,type字段的值为'table',name字段为表的名称。使用下面的语句可以等待所有表的列表:SELECTnameFROMsqlite_masterWHEREtype='table'ORDERBYname;对于索引,type='index',name为索引的名称,tbl_name为索引所属表的名称。对于表和索引,sql字段是创建表或索引的原始语句文本。对于自动创建的索引(通常使用PRIMARYKEY或UNIQUE创建),sql字段为NULL。SQLITE_MASTER表是只读的。您不能对此表使用UPDATE、INSERT或DELETE。该表由CREATETABLE、CREATEINDEX、DROPTABLE和DROPINDEX命令自动更新。临时表及其索引不在SQLITE_MASTER表中,而是在SQLITE_TEMP_MASTER中。SQLITE_TEMP_MASTER的工作方式类似于SQLITE_MASTER表,但仅对创建临时表的程序可见。要获取包含临时表的表,可以使用以下命令:SELECTnameFROM(SELECT*FROMsqlite_masterUNIONALLSELECT*FROMsqlite_temp_master)WHEREtype='table'ORDERBYname(10)IsthereaknownsizelimitforSQLite数据库?数据库大小限制为2TB(241字节)。这是理论上的限制。事实上,您应该将SQLite数据库的大小限制在100GB以下以避免性能问题。如果您需要在数据库中存储100GB或更多的数据,请考虑使用为此目的设计的企业数据库。数据库的理论行限制是264-1,显然您会在达到行限制之前超过文件大小限制。目前,一行可以存储230字节的数据。并且基本文件格式可以支持最大约262字节的行大小。表、索引或表和索引中的字段的数量可能有限制,但没有人知道有多少。事实上,每当打开一个新数据库时,SQLite都需要读取和解析所有表和索引声明的初始SQL,因此,为了在调用sqlite3_open()时获得最佳性能,最好减少声明表的数量。同样,即使表中的字段数没有限制,超过100个也太多了。只会优化表的前31个字段。您可以在索引中放入任意多的字段,但超过30个字段的索引将不会用于优化查询。表、索引、视图、触发器和字段名称可以任意长,但SQL函数名称(由sqlite3_create_function()API创建)不得超过255个字符。(11)SQLite中VARCHAR的最大长度是多少?SQLite不强制VARCHAR的长度。您可以声明一个VARCHAR(10),SQLite还允许您在其中存储500个字符。而且它们将始终完好无损——永远不会被截断。(12)SQLite是否支持BLOB类型?SQLite3.0版支持在任何字段中存储BLOB数据,而不管字段的声明类型如何。(13)如何在已有的SQLite数据表中添加/删除字段?SQLite具有有限的ALTERTABLE支持,可用于将列添加到表的末尾或更改表名。如果要对表的结构进行更复杂的更改,则需要重新创建表。您可以备份临时表中的数据,删除旧表,重建新表,然后恢复数据。例如,假设您有一个名为“t1”的表,其中包含名为“a”、“b”和“c”的字段,并且您想要删除字段“c”。按照以下步骤操作:开始事务;创建临时表t1_backup(a,b);插入t1_backup从t1中选择a,b;删除表t1;创建表t1(a,b);插入t1从t1_backup中选择a,b;DROPTABLEt1_backup;COMMIT;(14)我删除了很多数据但是数据库文件并没有减少。这是一个错误吗?不。当您从SQLite中删除数据时,未使用的磁盘空间会添加到内部“空闲列表”中,供您下次插入数据时使用。磁盘空间不会丢失,但磁盘空间不会返回给操作系统。如果删除大量数据,又想减小数据库文件的大小,执行VACUUM命令。VACUUM命令将清空“空闲列表”,将数据库的大小减小到最小。请注意,VACUUM需要一些时间(在Linux下大约0.5秒/兆字节)并且使用的磁盘空间是数据库文件大小的两倍。从SQLite版本3.1开始,VACUUM命令的替代方法是自动真空模式,它通过auto_vacuumpragma语法启用。(15)不支付版权费是否可以将SQLite用于商业用途?能。SQLite是公开的。没有声明对代码任何部分的所有权。你可以用它做任何你想做的事。(16)如何插入带单引号(')的字符串?只需使用双单引号,例如:INSERTINTOxyzvalueS('5O''clock');插入数据库的是:50'clock。(17)SQLITE_SCHEMA错误代表什么?在SQLite版本3中,当准备好的SQL语句无效且无法执行时,将返回SQLITE_SCHEMA错误。发生此错误时,应使用sqlite3_prepare()API函数重新编译该语句。在SQLite版本3中,仅在使用sqlite3_prepare()/sqlite3_step()/sqlite3_finalize()API函数执行SQL时会出现此错误,而在使用sqlite3_exec()时不会出现。这与版本2不同。大多数情况下,出现此错误是因为在SQL预处理完成时数据库已更改(可能由另一个进程更改)。也可能有以下原因:*对数据库进行DETACH操作*对数据库进行VACUUM操作*用户函数定义被删除或更改。*排序定义被删除或更改。*授权功能已更改。解决方案是重新编译并再次尝试执行。所有对sqlite3_prepare()/sqlite3_step()/sqlite3_finalize()API函数的引用都应该重新编译。请参见以下示例:intrc;sqlite3_stmt*pStmt;charzSql[]="SELECT.....";do{/*从SQL编译语句。假设成功。*/sqlite3_prepare(pDb,zSql,-1,&pStmt,0);while(SQLITE_ROW==sqlite3_step(pStmt)){/*对可用数据行做一些事情*/}/*完成语句。如果发生了SQLITE_SCHEMA错误**,则上述对sqlite3_step()的调用将**返回SQLITE_ERROR。sqlite3_finalize()将返回**SQLITE_SCHEMA。在这种情况下,循环将再次执行。*/rc=sqlite3_finalize(pStmt);}while(rc==SQLITE_SCHEMA);(18)为什么ROUND(9.95,1)返回9.9而不是10.0?9.95该不该扛起来?SQLite内部使用二进制算法,9.95在64位IEEE浮点数中表示为9.949999999999999289457264239899814128875732421875(SQLite内部使用)。因此,当您输入“9.95”时,SQLite将其解释为上面的数字并将其四舍五入为9.9。在处理浮点二进制数时总是会出现这个问题。一般规则是十进制有限浮点数通常不能表示为二进制有限浮点数,只能用最接近的二进制数代替。这个近似值会非常接近原来的数字,但总会有一些细微的差别,所以你可能得不到你期望的结果。
