SQLite添加主键


99

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

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

有没有一种方法可以在表创建期间或之后在Sqlite中添加主键?

所谓“在创建过程中”,是指在创建过程中使用CREATE TABLE AS


1
您可以使用任何数据库浏览器来编辑数据库。他们还删除并创建表。但我们不想为此而烦恼。您可以从此处sqlitebrowser.org
vichu

Answers:


121

创建SQLite表后,您不能以任何重大方式对其进行修改。可接受的建议解决方案是创建具有正确要求的新表,然后将数据复制到其中,然后删除旧表。

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


6
此链接(sqlite.org/omitted.html)详细解释了省略的内容。
Martin Velez,2012年

1
但我们可以添加新的栏目
Umesh制作


1
奇怪的是,您不能在创建表后添加PK,但是可以在创建CREATE UNIQUE INDEX pkName ON tableName(columnName)MS的数据库框架(例如MS SQL的SMO)使您在创建表之后添加PK 时添加索引()!
SteveCinq '17

@deFreitas请赐予我们智慧。显然,您希望人们知道您不同意答案或评论者之一说过的话,但是您的评论除了传达优越感和贪婪外,根本不包含任何信息。
内森·里德利

31

只要您使用CREATE TABLE,如果要在单个字段上创建主键,就可以使用:

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

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

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

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

此答案不解决表更改。


10

之后,我尝试通过直接更改sqlite_master表来添加主键。这个技巧似乎起作用。当然,这是一个hack解决方案。

简而言之:在表上创建一个常规(唯一)索引,然后使模式可写,并将索引名称更改为sqlite保留的形式以标识主键索引(即sqlite_autoindex_XXX_1,其中XXX是表名)并将sql字符串设置为NULL。最后,更改表定义本身。一个提示:在重新打开数据库之前,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;

一些测试(在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    

2
只是警告您,如果我做错了,您可以(就我所知)使整个数据库无法访问。我在玩耍,我不小心错过了第二个更新查询中的WHERE子句。SQLite不喜欢这样:P
Andrew Magee

7

根据有关表创建的sqlite 文档,将create table用作select会生成一个没有约束且没有主键的新表。

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

在大多数情况下,通过在数据库中创建唯一索引来实现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语法创建的表除外)都具有一个内部整数列,称为“ rowid”。根据文档,您可以使用此内部列来检索/修改记录表。


如果您使用EntityFramework连接到数据库,则它不会将唯一索引识别为主键。因此,尽管它在SQLite中在逻辑上和功能上是等效的,但并不是到处都等效。
克里斯汀·哈马克


3

介绍

这是基于Android的Java,它是更改数据库而又不会惹恼应用程序爱好者/客户的一个很好的例子。这基于“ SQLite常见问题解答”页面http://sqlite.org/faq.html#q11的想法

问题

我没有注意到需要设置row_number或record_id来删除收据中的单个已购买商品,与此同时,商品条形码编号也使我误以为将其用作删除该商品的钥匙。我将收据详细信息保存在表receive_barcode中。如果不使用record_id保留它,则意味着如果我使用商品条形码作为密钥,则删除收据中同一商品的所有记录。

注意

请理解,这是我撰写本文时正在处理的代码的复制粘贴。仅以它为例,随机粘贴将无济于事。首先根据您的需求进行修改

另外,请不要忘记阅读代码中的注释。

代码

使用此方法作为类中的方法来检查1st您要添加的列是否丢失。我们这样做只是为了不重复更改表receive_barcode的过程。只需在班级中提及它即可。在下一步中,您将看到我们将如何使用它。

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;
}

然后,如果您的应用程序的用户第一次没有退出表,则使用以下代码创建表receive_barcode 。并且请注意代码中的“如果不存在”。它很重要。

//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();
            }

1

我遇到了同样的问题,找到的最佳解决方案是首先创建定义主键的表,然后使用insert into语句。

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

INSERT INTO mytable 
SELECT field1, field2 
FROM anothertable;

大量插入的好主意
PirateApp '18

0

我使用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"

0

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


0
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>

0

使用像SQL Server的DB Browser这样的工具,它允许通过简单地右键单击表格->修改来添加PK,AI。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.