INTEGERPRIMARYKEY数据存储2010-06-1813:38:56阅读46评论0字号:大中小订阅.Sqlite中INTEGERPRIMARYKEYAUTOINCREMENT和rowid/INTEGERPRIMARYKEY的使用在用sqlite设计表时,每个表都有一个自己的整形id值作为主键,插入后能不能直接得到该主键呢?还有可不可以指定这么一个id值,因为sqlite内部本来就会为每个表加上一个rowid,这个rowid可以当成一个隐含的字段使用,但是由sqlite引擎来维护的,在3.0以前rowid是32位的整数,3.0以后是64位的整数,为什么不直接使用这个内部的rowid作为每个表的id主键呢。查了下文档。参照。EachentryinanSQLitetablehasaunique64-bitsignedintegerkeycalledtherowid.TherowidisalwaysavailableasanundeclaredcolumnnamedROWID,OID,or_ROWID_aslongasthosenamesarenotalsousedbyexplicitlydeclaredcolumns.IfthetablehasacolumnoftypeINTEGERPRIMARYKEYthenthatcolumnisanotheraliasfortherowid.如果表中有个INTEGERPRIMARYKEY字段,那么它只是rowid的别名。ThisroutinereturnstherowidofthemostrecentsuccessfulINSERTintothedatabasefromthedatabaseconnectioninthefirstargument.IfnosuccessfulINSERTshaveeveroccurredonthatdatabaseconnection,zeroisreturned.如果成功插入一条数据,会返回刚刚插入的数据的rowid.如果失败返回0.Android中如果发生错误返回的是-1参照:AcolumndeclaredINTEGERPRIMARYKEYwillautoincrement.Hereisthelonganswer:IfyoudeclareacolumnofatabletobeINTEGERPRIMARYKEY,thenwheneveryouinsertaNULLintothatcolumnofthetable,theNULLisautomaticallyconvertedintoanintegerwhichisonegreaterthanthelargestvalueofthatcolumnoverallotherrowsinthetable,or1ifthetableisempty.(Ifthelargestpossibleintegerkey,9223372036854775807,thenanunusedkeyvalueischosenatrandom.)Forexample,supposeyouhaveatablelikethis:CREATETABLEt1(aINTEGERPRIMARYKEY,bINTEGER);Withthistable,thestatementINSERTINTOt1VALUES(NULL,123);islogicallyequivalenttosaying:INSERTINTOt1VALUES((SELECTmax(a)FROMt1)+1,123);Thereisafunctionnamedsqlite3_last_insert_rowid()whichwillreturntheintegerkeyforthemostrecentinsertoperation.Notethattheintegerkeyisonegreaterthanthelargestkeythatwasinthetablejustpriortotheinsert.Thenewkeywillbeuniqueoverallkeyscurrentlyinthetable,butitmightoverlapwithkeysthathavebeenpreviouslydeletedfromthetable.Tocreatekeysthatareuniqueoverthelifetimeofthetable,addtheAUTOINCREMENTkeywordtotheINTEGERPRIMARYKEYdeclaration.Thenthekeychosenwillbeonemorethanthanthelargestkeythathaseverexistedinthattable.Ifthelargestpossiblekeyhaspreviouslyexistedinthattable,thentheINSERTwillfailwithanSQLITE_FULLerrorcode.把一个列申明为INTEGERPRIMARYKEY,那么在向它插入NULL,该列就由系统指定。该值为已经存在的数据的该列的最大值加1。空表时该值就为1如果该值已经超过了最大值,那么它会随即选择一个已存数据没使用过的值做个插入数据的值。如果用户在插入时给它指定一个值,那么返回的也是那个值。因为本来返回的应该是rowid,但如果表中有个INTEGERPRIMARYKEY字段,那么它只是rowid的别名。所以返回的就是指定的那个值。把一个列声明为INTEGERPRIMARYKEYAUTOINCREMENT的话,它的值是选择的在该表中曾经使用过的最大值+1。如果达到了最大值的话,会插入失败,并抛出anSQLITE_FULLerrorcode。再参照sqlite的文档:最后得出以下结论:用自增长字段为主键有不少问题,比如维护或是在大型分布应用中主键冲突的解决等。在一些大型分布应用中主键一般选用guid,这可以有效的避免主键冲突,减少对主键维护的工程当然,对于中小型的应用,自增长字段的好处更多一些,简单、快速。Sqlite中,一个自增长字段定义为INTEGERPRIMARYKEYAUTOINCREMENT或者INTEGERPRIMARYKEY时,那么在插入一个新数据时,只需要将这个字段的值指定为NULL,即可由引擎自动设定其值。当然,也可以设置为非NULL的数字来自己指定这个值,但这样就必须自己小心,不要引起冲突。当这个rowid的值大于所能表达的最大值9223372036854775807(3.0及以后版本的rowid最大值)后,rowid的新值会这个最大数之前随机找一个没被使用了的值。所以在rowid达到最大值前,rowid的值是严格单调增加的。INTEGERPRIMARYKEYAUTOINCREMENT自增长字段的算法与rowid/INTEGERPRIMARYKEY稍微有些不同。第一,在达到最大值后,rowid/INTEGERPRIMARYKEY会找已被删除的字段对应的rowid/INTEGERPRIMARYKEY作为新值,而自增长字段则会丢出一个SQLITE_FULL的错误。第二,自增长字段在增加新值时,是找一个从没被使用过的值作为新值,而rowid/INTEGERPRIMARYKEY则是找最大已存在的(rowid/INTEGERPRIMARYKEY)+1。这里对应用的影响会比较大,尤其是一些对id值有依赖的元记录,只适合使用自增长字段而不能用rowid/INTEGERPRIMARYKEY。比如,我们设计一个元记录表:Createtablemeta_struct(idINTEGERPRIMARYKEYAUTOINCREMENT,namevarchar,typeInteger);然后,定义一个一级表,来描述其它表的结构:Createtablemeta_table(tableidINTEGER,table_fieldinteger)最后,我们的应用可以根据这个一级表来产生实际使用的二级表。这样为保证兼容性meta_struct中的id必须是唯一的,如果有字段被删除,也不能重复使用这个字段的id值,不然,在数据库合并时,一级表和二级表就会混乱。所以meta_struct表中的主键只能使用自增长字段,而不能用rowid。第三,使用自增长字段,引擎会自动产生一个sqlite_sequence表,用于记录每个表的自增长字段的已使用的最大值,用户可以看到,并可以用使用Update、Delete和Insert操作,但不建议这么使用,这会让引擎混乱。如果使用rowid/INTEGERPRIMARYKEY,也会有这么一个内部表,用户可以维护rowid/INTEGERPRIMARYKEY值,但看不到。这么看来,如果直接使用rowid/INTEGERPRIMARYKEY来代替自增加字段,根据两者的细微的差别,需要注意是否与自己的应用冲突,如果没有冲突,那么用rowid/INTEGERPRIMARYKEY会更快一第四,在android中,对于INTEGERPRIMARYKEY,如果插入数据A,B,C,它们的_id为1,2,3,那么如果把他们都删除了,再插入一条数据,那么它的id为1而不是4。sqlite3apisqlite3_busy_timeout与sqlite3_busy_handler的使用与区别收藏在用多数据连接方式使用sqlite时,常常会遇到SQLITE_BUSY的错误,这是由于使用当前连接访问数据时,要申请相应级别的锁,而各个级别的锁有些是互斥的,当申请不到锁时就会返回这个错误。这时只要稍等片刻,等其它连接的操作处理完,释放了相斥的锁之后就可以取得锁并进行操作了。但是sqlite3中并未对出现sqlite_busy后重试做默认的处理,而是提供了一种处理机制busyhandle。有两个api可以创建busyhandle。intsqlite3_busy_handler(sqlite3*,int(*)(void*,int),void*)函数可以定义一个回调函数,当出现数据库忙时,sqlite会调用该函数当回调函数为NULL时,清除busyhandle,申请不到锁直接返回回调函数的第二个函数会被传递为该由此次忙事件调用该函数的次数回调函数返回非0,数据库会重试当前操作,返回0则当前操作返回SQLITE_BUSYintsqlite3_busy_timeout(sqlite3*,intms);定义一个毫秒数,当未到达该毫秒数时,sqlite会sleep并重试当前操作如果超过ms毫秒,仍然申请不到需要的锁,当前操作返回sqlite_BUSY当ms=0时,清除busyhandle,申请不到锁直接返回从上面可以得知,一个函数可以控制超时的次数,一个函数可以控制超时的时间但对于一个连接来说,只能有一个busyhandle,所以两个函数会相互影响,设置一个的同时会清除另一个,应根据需要来选择。很多人用这个函数没有成功,其实只要你仔细查看sqlite的源码就会发现,这个函数实际上注册了一个默认的sqlite3_busy_handler(sqliteDefaultBusyCallback),第二个参数必段要大于1000且是他的整数倍才有意义,由于此默认callback函数延时较大,建议自己写sqlite3_busy_handler处理databaseislocked的问题这个函数实际上注册了一个默认的sqlite3_busy_handler(sqliteDefaultBusyCallback),而这个回调函数在你的编译环境下可能使得第二个ms参数必需要