php SQLite学习笔记与常见问题分析

前端之家收集整理的这篇文章主要介绍了php SQLite学习笔记与常见问题分析前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

直到学会!学之前找资料

sqlite的sql

ATTACHDATABASE
BEGINTRANSACTION
comment
COMMITTRANSACTION
COPY
CREATEINDEX
CREATETABLE
CREATETRIGGER
CREATEVIEW
DELETE
DETACHDATABASE
DROPINDEX
DROPTABLE
DROPTRIGGER
DROPVIEW
ENDTRANSACTION
EXPLAIN
expression
INSERT
ONCONFLICTclause
PRAGMA
REPLACE
ROLLBACKTRANSACTION
SELECT
UPDATE
sqlite常见问题

(1)如何建立自动增长字段?

简短回答:声明为INTEGERPRIMARYKEY的列将会自动增长。 长一点的答案:如果你声明表的一列为INTEGERPRIMARYKEY,那么,每当你在该列上插入一NULL值时,NULL自动被转换为一个比该列中最大值大1的一个整数,如果表是空的,将会是1。(如果是最大可能的主键9223372036854775807,那个,将键值将是随机未使用的数。)如,有下列表: CREATETABLEt1(
aINTEGERPRIMARYKEY,
bINTEGER
); 在该表上,下列语句 INSERTINTOt1VALUES(NULL,123); 在逻辑上等价于: INSERTINTOt1VALUES((SELECTmax(a)FROMt1)+1,123); 有一个新的API叫做sqlite3_last_insert_rowid(),它将返回最近插入的整数值。 注意该整数会比表中该列上的插入之前的最大值大1。该键值在当前的表中是唯一的。但有可能与已从表中删除的值重叠。要想建立在整个表的生命周期中唯一的键值,需要在INTEGERPRIMARYKEY上增加AUTOINCREMENT声明。那么,新的键值将会比该表中曾能存在过的最大值大1。如果最大可能的整数值在数据表中曾经存在过,INSERT将会失败,并返回sqlITE_FULL错误代码
-------------------------------------------------------------------------------- (2)sqlite3支持何种数据类型? NULL
INTEGER
REAL
TEXT
BLOB
但实际上,sqlite3也接受如下的数据类型:
smallint16位元的整数。
interger32位元的整数。
decimal(p,s)p精确值和s大小的十进位整数,精确值p是指全部有几个数(digits)大小值,s是指小数点後有几位数。如果没有特别指定,则系统会设为p=5;s=0。
float32位元的实数。
double64位元的实数。
char(n)n长度的字串,n不能超过254。
varchar(n)长度不固定且其最大长度为n的字串,n不能超过4000。
graphic(n)和char(n)一样,不过其单位是两个字元double-bytes,n不能超过127。这个形态是为了支援两个字元长度的字体,例如中文字。
vargraphic(n)可变长度且其最大长度为n的双字元字串,n不能超过2000。
date包含了年份、月份、日期。
time包含了小时、分钟、秒。
timestamp包含了年、月、日、时、分、秒、千分之一秒。 参见http://www.sqlite.org/datatype3.html.
-------------------------------------------------------------------------------- (3)sqlite允许向一个integer型字段中插入字符串! 这是一个特性,而不是一个bug。sqlite不强制数据类型约束。任何数据都可以插入任何列。你可以向一个整型列中插入任意长度的字符串,向布尔型列中插入浮点数,或者向字符型列中插入日期型值。在CREATETABLE中所指定的数据类型不会限制在该列中插入任何数据。任何列均可接受任意长度的字符串(只有一种情况除外:标志为INTEGERPRIMARYKEY的列只能存储64位整数,当向这种列中插数据除整数以外的数据时,将会产生错误。 但sqlite确实使用声明的列类型来指示你所期望的格式。所以,例如你向一个整型列中插入字符串时,sqlite会试图将该字符串转换成一个整数。如果可以转换,它将插入该整数;否则,将插入字符串。这种特性有时被称为类型或列亲和性(typeorcolumnaffinity).
-------------------------------------------------------------------------------- (4)为什么sqlite不允许在同一个表不同的两行上使用0和0.0作主键? 主键必须是数值类型,将主键改为TEXT型将不起作用。 每一行必须有一个唯一的主键。对于一个数值型列,sqlite认为'0'和'0.0'是相同的,因为他们在作为整数比较时是相等的(参见上一问题)。所以,这样值就不唯一了。
-------------------------------------------------------------------------------- (5)多个应用程序或一个应用程序的多个实例可以同时访问同一个数据库文件吗? 多个进程可同时打开同一个数据库。多个进程可以同时进行SELECT操作,但在任一时刻,只能有一个进程对数据库进行更改。 sqlite使用读、写锁控制对数据库的访问。(在Win95/98/ME等不支持读、写锁的系统下,使用一个概率性的模拟来代替。)但使用时要注意:如果数据库文件存放于一个NFS文件系统上,这种锁机制可能不能正常工作。这是因为fcntl()文件锁在很多NFS上没有正确的实现。在可能有多个进程同时访问数据库的时候,应该避免将数据库文件放到NFS上。在Windows上,Microsoft的文档中说:如果使用FAT文件系统而没有运行share.exe守护进程,那么锁可能是不能正常使用的。那些在Windows上有很多经验的人告诉我:对于网络文件文件锁的实现有好多Bug,是靠不住的。如果他们说的是对的,那么在两台或多台Windows机器间共享数据库可能会引起不期望的问题。 我们意识到,没有其它嵌入式的sql数据库引擎能象sqlite这样处理如此多的并发。sqlite允许多个进程同时打开一个数据库,同时读一个数据库。当有任何进程想要写时,它必须在更新过程中锁住数据库文件。但那通常只是几毫秒的时间。其它进程只需等待写进程干完活结束。典型地,其它嵌入式的sql数据库引擎同时只允许一个进程连接到数据库。 但是,Client/Server数据库引擎(如Postgresql,MysqL,或Oracle)通常支持更高级别的并发,并且允许多个进程同时写同一个数据库。这种机制在Client/Server结构的数据库上是可能的,因为总是有一个单一的服务器进程很好地控制、协调对数据库的访问。如果你的应用程序需要很多的并发,那么你应该考虑使用一个Client/Server结构的数据库。但经验表明,很多应用程序需要的并发,往往比其设计者所想象的少得多。 当sqlite试图访问一个被其它进程锁住的文件时,缺省的行为是返回sqlITE_BUSY。可以在C代码中使用sqlite3_busy_handler()或sqlite3_busy_timeout()API函数调整这一行为。
--------------------------------------------------------------------------- (6)sqlite线程安全吗? 线程是魔鬼(Threadsareevil)。避免使用它们。 sqlite是线程安全的。由于很多用户会忽略我们在上一段中给出的建议,我们做出了这种让步。但是,为了达到线程安全,sqlite在编译时必须将sqlITE_THREADSAFE预处理宏置为1。在Windows和Linux上,已编译的好的二进制发行版中都是这样设置的。如果不确定你所使用的库是否是线程安全的,可以调用sqlite3_threadsafe()接口找出。 在3.3.1版本之前,一个sqlite3结构只能被用于调用sqlite3_open创建的同一线程。你不能在一个线程中打开数据库,然后将数据库句柄传递给另外一个进程使用。这主要是由于在好多通用的线程实现(如RedHat9)中的限制引起的(是Bug吗?)。特别的,在有问题的系统上,一个进程创建的fcntl()锁无法被其它线程清除或修改。所以,由于sqlite大量使用fcntl()锁做并发控制,如果你在不同的线程间移动数据库连接,就可能会出现严重的问题。 在3.3.1版本上,关于在线程间移动数据库连接的限制变得宽松了。在它及以后的版本中,只要连接没有持有fcntl()锁,在线程间移动句柄是安全的。如果没有未决的事务,并且所有的语句都已执行完毕,你就可以安全的假定不再持有任何锁。 在UNIX中,在执行fork()系统调用时不应携带已打开的数据库进入子进程。那样做将会有问题。
-------------------------------------------------------------------------------- (7)在sqlite数据库中如何列出所有的表和索引? 如果你运行sqlite3命令行来访问你的数据库,可以键入“.tables”来获得所有表的列表。或者,你可以输入“.schema”来看整个数据库模式,包括所有的表的索引。输入这些命令,后面跟一个LIKE模式匹配可以限制显示的表。 在一个C/C++程序中(或者脚本语言使用Tcl/Ruby/Perl/Python等)你可以在一个特殊的名叫sqlITE_MASTER上执行一个SELECT查询以获得所有表的索引。每一个sqlite数据库都有一个叫sqlITE_MASTER的表,它定义数据库的模式。sqlITE_MASTER表看起来如下: CREATETABLEsqlite_master(
typeTEXT,
nameTEXT,
tbl_nameTEXT,
rootpageINTEGER,
sqlTEXT
); 对于表来说,type字段永远是'table',name字段永远是表的名字。所以,要获得数据库中所有表的列表,使用下列SELECT语句: SELECTnameFROMsqlite_master
WHEREtype='table'
ORDERBYname; 对于索引,type等于'index',name则是索引的名字,tbl_name是该索引所属的表的名字。不管是表还是索引,sql字段是原先用CREATETABLE或CREATEINDEX语句创建它们时的命令文本。对于自动创建的索引(用来实现PRIMARYKEY或UNIQUE约束),sql字段为NULL。 sqlITE_MASTER表是只读的。不能对它使用UPDATE、INSERT或DELETE。它会被CREATETABLE、CREATEINDEX、DROPTABLE和DROPINDEX命令自动更新。 临时表不会出现在sqlITE_MASTER表中。临时表及其索引和触发器存放在另外一个叫sqlITE_TEMP_MASTER的表中。sqlITE_TEMP_MASTER跟sqlITE_MASTER差不多,但它只是对于创建那些临时表的应用可见。如果要获得所有表的列表,不管是永久的还是临时的,可以使用类似下面的命令: SELECTnameFROM
(SELECTFROMsqlite_masterUNIONALL
SELECT
FROMsqlite_temp_master)
WHEREtype='table'
ORDERBYname
-------------------------------------------------------------------------------- (8)sqlite数据库有已知的大小限制吗? 在Windows和Unix下,版本2.7.4的sqlite可以达到2的41次方字节(2T字节)。老版本的为2的31次方字节(2G字节)。
sqlite版本2.8限制一个记录的容量为1M。sqlite版本3.0则对单个记录容量没有限制。
表名、索引表名、视图名、触发器名和字段名没有长度限制。但sql函数名称(由sqlite3_create_function()API函数创建)不得超过255个字符。 对有关sqlite限制的详细讨论,见limits.html。
-------------------------------------------------------------------------------- (9)在sqlite中,VARCHAR字段最长是多少? sqlite不强制VARCHAR的长度。你可以在sqlITE中声明一个VARCHAR(10),sqlite还是可以很高兴地允许你放入500个字符。并且这500个字符是原封不动的,它永远不会被截断。
-------------------------------------------------------------------------------- (10)sqlite支持二进制大对象吗? sqlite3.0及以后版本允许你在任何列中存储BLOB数据。即使该列被声明为其它类型也可以。
-------------------------------------------------------------------------------- (11)在sqlite中,如何在一个表上添加删除一列? sqlite有有限地ALTERTABLE支持。你可以使用它来在表的末尾增加一列,可更改表的名称。如果需要对表结构做更复杂的改变,则必须重新建表。重建时可以先将已存在的数据放到一个临时表中,删除原表,创建新表,然后将数据从临时表中复制回来。 如,假设有一个t1表,其中有"a","b","c"三列,如果要删除列c,以下过程描述如何做: BEGINTRANSACTION;
CREATETEMPORARYTABLEt1_backup(a,b);
INSERTINTOt1_backupSELECTa,bFROMt1;
DROPTABLEt1;
CREATETABLEt1(a,b);
INSERTINTOt1SELECTa,bFROMt1_backup;
DROPTABLEt1_backup;
COMMIT;
-------------------------------------------------------------------------------- (12)我在数据库删除了很多数据,但数据库文件没有变小,是Bug吗? 不是。当你从sqlite数据库删除数据时,未用的磁盘空间将会加入一个内部的“自由列表”中。当你下次插入数据时,这部分空间可以重用。磁盘空间不会丢失,但也不会返还给操作系统。 如果删除了大量数据,而又想缩小数据库文件占用的空间,执行VACUUM命令。VACUUM将会从头重新组织数据库。这将会使用数据库有一个空的“自由链表”,数据库文件也会最小。但要注意的是,VACUUM的执行会需要一些时间(在sqlite开发时,在Linux上,大约每M字节需要半秒种),并且,执行过程中需要原数据库文件至多两倍的临时磁盘空间。 对于sqlite3.1版本,一个auto-vacumm模式可以替代VACUUM命令。可以使用auto_vacuumpragma打开。
-------------------------------------------------------------------------------- (13)我可以在商业产品中使用sqlite而不需支付许可费用吗? 是的。sqlite在publicdomain。对代码的任何部分没有任何所有权声明。你可以使用它做任何事。
-------------------------------------------------------------------------------- (14)如何在字符串中使用单引号(')? sql标准规定,在字符串中,单引号需要使用逃逸字符,即在一行中使用两个单引号。在这方面sql用起来类似Pascal语言。sqlite尊循标准。如: INSERTINTOxyzVALUES('5O''clock');
-------------------------------------------------------------------------------- (15)sqlITE_SCHEMAerror是什么错误?为什么会出现该错误? 当一个准备好的(prepared)sql语句不再有效或者无法执行时,将返回一个sqlITE_SCHEMA错误。发生该错误时,sql语句必须使用sqlite3_prepare()API来重新编译.在sqlite3中,一个sqlITE_SCHEMA错误只会发生在用sqlite3_prepare()/sqlite3_step()/sqlite3_finalize()API执行sql时。而不会发生在使用sqlite3_exec()时。在版本2中不是这样。 准备好的语句失效的最通常原因是:在语句准备好后,数据库的模式又被修改了。另外的原因会发生在: 数据库离线:DETACHed.
数据库被VACUUMed
一个用户存储过程定义被删除或改变。
一个collation序列定义被删除或改变。
认证函数被改变。
在所有情况下,解决方法是重新编译并执行该sql语句。因为一个已准备好的语句可以由于其它进程改变数据库模式而失效,所有使用sqlite3_prepare()/sqlite3_step()/sqlite3_finalize()API的代码都应准备处理sqlITE_SCHEMA错误。下面给出一个例子: intrc;
sqlite3_stmtpStmt;
charzsql[]="SELECT....."; do{
/
Compilethestatementfromsql.Assumesuccess./
sqlite3_prepare(pDb,zsql,-1,&pStmt,0); while(sqlITE_ROW==sqlite3_step(pStmt)){
/
Dosomethingwiththerowofavailabledata/
} /
Finalizethestatement.IfansqlITE_SCHEMAerrorhas
occured,thentheabovecalltosqlite3_step()willhave
returnedsqlITE_ERROR.sqlite3_finalize()willreturn
*sqlITE_SCHEMA.Inthiscasetheloopwillexecuteagain.
/
rc=sqlite3_finalize(pStmt);
}while(rc==sqlITE_SCHEMA); -------------------------------------------------------------------------------- (16)为什么ROUND(9.95,1)返回9.9而不是10.0?9.95不应该圆整(四舍五入)吗? sqlite使用二进制算术,在二进制中,无法用有限的二进制位数表示9.95。使用64-bitIEEE浮点(sqlite就是使用这个)最接近9.95的二进制表示是9.949999999999999289457264239899814128875732421875。所在,当你输入9.95时,sqlite实际上以为是上面的数字,在四舍五入时会舍去。 这种问题在使用二进制浮点数的任何时候都会出现。通常的规则是记住很多有限的十进制小数都没有一个对应的二进制表示。所以,它们只能使用最接近的二进制数。它们通常非常接近,但也会有些微小的不同,有些时候也会导致你所期望的不同的结果。

猜你在找的PHP相关文章