SQLite添加主键

我在Sqlite中创build了一个表,使用CREATE TABLE AS语法创build一个基于SELECT语句的表。 现在这个表没有主键,但我想添加一个。

执行ALTER TABLE table_name ADD PRIMARY KEY(col1, col2,...)给出语法错误“near PRIMARY”

有没有办法添加一个主键在创build表或之后在Sqlite?

编辑:“在创build过程中”我的意思是在创build过程中与CREATE TABLE AS

在创buildSQLite表之后,不能以任何有意义的方式修改SQLite表。 接受的build议解决scheme是创build一个具有正确要求的新表,并将数据复制到该表中,然后删除旧表。

这里是关于这个官方文档: http : //sqlite.org/faq.html#q11

只要您使用的是CREATE TABLE ,如果您要在单个字段上创build主键,则可以使用:

 CREATE TABLE mytable ( field1 TEXT, field2 INTEGER PRIMARY KEY, field3 BLOB, ); 

使用CREATE TABLE ,您也可以始终使用以下方法在一个或多个字段上创build主键:

 CREATE TABLE mytable ( field1 TEXT, field2 INTEGER, field3 BLOB, PRIMARY KEY (field2, field1) ); 

参考: http : //www.sqlite.org/lang_createtable.html

这个答案不涉及表格的变更。

我试图通过直接更改sqlite_master表来添加主键。 这个伎俩似乎工作。 当然这是一个黑客解决scheme。

简而言之:在表上创build一个常规(唯一)索引,然后使该模式可写,并将索引名称更改为sqlite保留的格式以标识主键索引(即sqlite_autoindex_XXX_1,其中XXX是表名)并将sqlstring设置为NULL。 最后改变表格定义本身。 一个pittfal:在数据库重新打开之前,sqlite没有看到索引名称的变化。 这看起来像一个错误,但不是一个严重的错误(即使没有重新打开数据库,你仍然可以使用它)。

假设表格如下所示:

 CREATE TABLE tab1(i INTEGER, j INTEGER, t TEXT); 

然后我做了以下几点:

 BEGIN; CREATE INDEX pk_tab1 ON tab1(i,j); pragma writable_schema=1; UPDATE sqlite_master SET name='sqlite_autoindex_tab1_1',sql=null WHERE name='pk_tab1'; UPDATE sqlite_master SET sql='CREATE TABLE tab1(i integer,j integer,t text,primary key(i,j))' WHERE name='tab1'; COMMIT; 

一些testing(在sqlite shell中):

 sqlite> explain query plan select * from tab1 order by i,j; 0|0|0|SCAN TABLE tab1 USING INDEX sqlite_autoindex_tab1_1 sqlite> drop index sqlite_autoindex_tab1_1; Error: index associated with UNIQUE or PRIMARY KEY constraint cannot be dropped 

你可以这样做:

 CREATE TABLE mytable ( field1 text, field2 text, field3 integer, PRIMARY KEY (field1, field2) ); 

根据关于表创build的sqlite 文档 ,使用create table作为select产生一个没有约束且没有主键的新表。

但是,文档还指出,主键和唯一索引在逻辑上是等效的( 请参阅约束部分 ):

在大多数情况下,通过在数据库中创build唯一索引来实现UNIQUE和PRIMARY KEY约束。 (例外是在WITHOUT ROWID表上的INTEGER PRIMARY KEY和PRIMARY KEY。)因此,以下模式在逻辑上是等效的:

 CREATE TABLE t1(a, b UNIQUE); CREATE TABLE t1(a, b PRIMARY KEY); CREATE TABLE t1(a, b); CREATE UNIQUE INDEX t1b ON t1(b); 

因此,即使无法通过SQL alter语法来更改表定义,也可以通过使用唯一索引来获得相同的主键效果。

此外,任何表(除了没有使用rowid语法创build的表)都有一个名为“rowid”的内部整数列。 根据文档,你可以使用这个内部列来检索/修改logging表。

介绍

这是基于Android的Java,这是一个很好的例子,而不用烦扰你的应用程序粉丝/客户改变数据库。 这是基于SQLite常见问题页面http://sqlite.org/faq.html#q11的想法;

问题

我没有注意到我需要设置一个row_number或者record_id来删除收据中的一个购买物品,同时这个物品条形码让我想到了把它作为删除这个物品的关键。 我在收据表receipt_barcode保存收据的详细信息。 离开它没有record_id可能意味着删除收据中的相同项目的所有logging,如果我使用项目条形码作为关键。

注意

请理解,这是我写这篇文章的时候我的代码的复制粘贴。 只用它作为一个例子,随意复制粘贴不会帮助你。 首先根据您的需要进行修改

另外请不要忘记阅读代码中的评论。

代码

使用这个作为你的class级的方法来检查你想添加的列是否丢失。 我们这样做只是为了不重复更改表receipt_barcode的过程。 只要提到它作为你的class级的一部分。 下一步,你会看到我们将如何使用它。

 public boolean is_column_exists(SQLiteDatabase mDatabase , String table_name, String column_name) { //checks if table_name has column_name Cursor cursor = mDatabase.rawQuery("pragma table_info("+table_name+")",null); while (cursor.moveToNext()){ if (cursor.getString(cursor.getColumnIndex("name")).equalsIgnoreCase(column_name)) return true; } return false; } 

然后,下面的代码用于创build表receipt_barcode如果它已经不是第一次你的应用程序的用户退出。 请注意代码中的“IF NOT EXISTS”。 这很重要。

 //mDatabase should be defined as a Class member (global variable) //for ease of access : //SQLiteDatabse mDatabase=SQLiteDatabase.openOrCreateDatabase(dbfile_path, null); creation_query = " CREATE TABLE if not exists receipt_barcode ( "; creation_query += "\n record_id INTEGER PRIMARY KEY AUTOINCREMENT,"; creation_query += "\n rcpt_id INT( 11 ) NOT NULL,"; creation_query += "\n barcode VARCHAR( 255 ) NOT NULL ,"; creation_query += "\n barcode_price VARCHAR( 255 ) DEFAULT (0),"; creation_query += "\n PRIMARY KEY ( record_id ) );"; mDatabase.execSQL(creation_query); //This is where the important part comes in regarding the question in this page: //adding the missing primary key record_id in table receipt_barcode for older versions if (!is_column_exists(mDatabase, "receipt_barcode","record_id")){ mDatabase.beginTransaction(); try{ Log.e("record_id", "creating"); creation_query="CREATE TEMPORARY TABLE t1_backup("; creation_query+="record_id INTEGER PRIMARY KEY AUTOINCREMENT,"; creation_query+="rcpt_id INT( 11 ) NOT NULL,"; creation_query+="barcode VARCHAR( 255 ) NOT NULL ,"; creation_query+="barcode_price VARCHAR( 255 ) NOT NULL DEFAULT (0) );"; mDatabase.execSQL(creation_query); creation_query="INSERT INTO t1_backup(rcpt_id,barcode,barcode_price) SELECT rcpt_id,barcode,barcode_price FROM receipt_barcode;"; mDatabase.execSQL(creation_query); creation_query="DROP TABLE receipt_barcode;"; mDatabase.execSQL(creation_query); creation_query="CREATE TABLE receipt_barcode ("; creation_query+="record_id INTEGER PRIMARY KEY AUTOINCREMENT,"; creation_query+="rcpt_id INT( 11 ) NOT NULL,"; creation_query+="barcode VARCHAR( 255 ) NOT NULL ,"; creation_query+="barcode_price VARCHAR( 255 ) NOT NULL DEFAULT (0) );"; mDatabase.execSQL(creation_query); creation_query="INSERT INTO receipt_barcode(record_id,rcpt_id,barcode,barcode_price) SELECT record_id,rcpt_id,barcode,barcode_price FROM t1_backup;"; mDatabase.execSQL(creation_query); creation_query="DROP TABLE t1_backup;"; mDatabase.execSQL(creation_query); mdb.setTransactionSuccessful(); } catch (Exception exception ){ Log.e("table receipt_bracode", "Table receipt_barcode did not get a primary key (record_id"); exception.printStackTrace(); } finally { mDatabase.endTransaction(); } 

我使用CREATE TABLE AS语法来合并多个列,并遇到同样的问题。 这里是我写的一个AppleScript来加速这个过程。

 set databasePath to "~/Documents/Databases/example.db" set tableOne to "separate" -- Table from which you are pulling data set tableTwo to "merged" -- Table you are creating set {tempCol, tempColEntry, permColEntry} to {{}, {}, {}} set permCol to {"id integer primary key"} -- Columns are created from single items AND from the last item of a list -- {{"a", "b", "c"}, "d", "e"} Columns "a" and "b" will be merged into a new column "c". tableTwo will have columns "c", "d", "e" set nonCoal to {"City", "Contact", "Names", {"Address 1", "Address", "address one", "Address1", "Text4", "Address 1"}, {"E-Mail", "E-Mail Address", "Email", "Email Address", "EmailAddress", "Email"}, {"Zip", "Zip Code", "ZipCode", "Zip"}, {"Telephone", "BusinessPhone", "Phone", "Work Phone", "Telephone"}, {"St", "State", "State"}, {"Salutation", "Mr/Ms", "Mr/s", "Salutations", "Sautation", "Salutation"}} -- Build the COALESCE statements repeat with h from 1 to count of nonCoal set aColumn to item h of nonCoal if class of aColumn is not list then if (count of words of aColumn) > 1 then set aColumn to quote & aColumn & quote set end of tempCol to aColumn set end of permCol to aColumn else set coalEntry to {} repeat with i from 1 to count of aColumn set coalCol to item i of aColumn as string if (count of words of coalCol) > 1 then set coalCol to quote & coalCol & quote if i = 1 then set end of coalEntry to "TRIM(COALESCE(" & coalCol & ", '') || \" \" || " else if i < ((count of aColumn) - 1) then set end of coalEntry to "COALESCE(" & coalCol & ", '') || \" \" || " else if i = ((count of aColumn) - 1) then set as_Col to item (i + 1) of aColumn as string if (count of words of as_Col) > 1 then set as_Col to quote & as_Col & quote set end of coalEntry to ("COALESCE(" & coalCol & ", '')) AS " & as_Col) & "" set end of permCol to as_Col end if end repeat set end of tempCol to (coalEntry as string) end if end repeat -- Since there are ", '' within the COALESCE statement, you can't use "TID" and "as string" to convert tempCol and permCol for entry into sqlite3. I rebuild the lists in the next block. repeat with j from 1 to count of tempCol if j < (count of tempCol) then set end of tempColEntry to item j of tempCol & ", " set end of permColEntry to item j of permCol & ", " else set end of tempColEntry to item j of tempCol set end of permColEntry to item j of permCol end if end repeat set end of permColEntry to ", " & item (j + 1) of permCol set permColEntry to (permColEntry as string) set tempColEntry to (tempColEntry as string) -- Create the new table with an "id integer primary key" column set createTable to "create table " & tableTwo & " (" & permColEntry & "); " do shell script "sqlite3 " & databasePath & space & quoted form of createTable -- Create a temporary table and then populate the permanent table set createTemp to "create temp table placeholder as select " & tempColEntry & " from " & tableOne & "; " & "insert into " & tableTwo & " select Null, * from placeholder;" do shell script "sqlite3 " & databasePath & space & quoted form of createTemp --export the new table as a .csv file do shell script "sqlite3 -header -column -csv " & databasePath & " \"select * from " & tableTwo & " ; \"> ~/" & tableTwo & ".csv" 

我有同样的问题,我发现最好的解决scheme是首先创build表定义主键,然后使用插入语句。

 CREATE TABLE mytable ( field1 INTEGER PRIMARY KEY, field2 TEXT ); INSERT INTO mytable SELECT field1, field2 FROM anothertable; 
 sqlite> create table t(id int, col2 varchar(32), col3 varchar(8)); sqlite> insert into t values(1, 'he', 'ha'); sqlite> sqlite> create table t2(id int primary key, col2 varchar(32), col3 varchar(8)); sqlite> insert into t2 select * from t; sqlite> .schema CREATE TABLE t(id int, col2 varchar(32), col3 varchar(8)); CREATE TABLE t2(id int primary key, col2 varchar(32), col3 varchar(8)); sqlite> drop table t; sqlite> alter table t2 rename to t; sqlite> .schema CREATE TABLE IF NOT EXISTS "t"(id int primary key, col2 varchar(32), col3 varchar(8)); sqlite> 

我认为在该列上添加索引可以获得相同的效果。