SQLite เพิ่มคีย์หลัก


100

ฉันสร้างตารางใน Sqlite โดยใช้CREATE TABLE ASไวยากรณ์เพื่อสร้างตารางตามSELECTคำสั่ง ตอนนี้ตารางนี้ไม่มีคีย์หลัก แต่ฉันต้องการเพิ่ม

การดำเนินการALTER TABLE table_name ADD PRIMARY KEY(col1, col2,...)ทำให้เกิดข้อผิดพลาดทางไวยากรณ์ "near PRIMARY"

มีวิธีเพิ่มคีย์หลักระหว่างการสร้างตารางหรือหลังจากนั้นใน Sqlite หรือไม่?

โดย "ในระหว่างการสร้าง" CREATE TABLE ASผมหมายความว่าในระหว่างการสร้างด้วย


1
คุณสามารถใช้เบราว์เซอร์ db เพื่อแก้ไขฐานข้อมูล พวกเขายังลบและสร้างตาราง แต่เราไม่อยากรบกวนมัน คุณสามารถดาวน์โหลดเบราว์เซอร์ db สำหรับระบบปฏิบัติการใด ๆ จากที่นี่sqlitebrowser.org
vichu

คำตอบ:


122

คุณไม่สามารถแก้ไขตาราง SQLite ได้อย่างมีนัยสำคัญหลังจากสร้างตารางแล้ว วิธีแก้ปัญหาที่ได้รับการแนะนำคือการสร้างตารางใหม่ที่มีข้อกำหนดที่ถูกต้องและคัดลอกข้อมูลของคุณลงในตารางจากนั้นวางตารางเก่า

นี่คือเอกสารอย่างเป็นทางการเกี่ยวกับเรื่องนี้: http://sqlite.org/faq.html#q11


7
ลิงก์นี้ ( sqlite.org/omitted.html ) อธิบายสิ่งที่ถูกละไว้โดยละเอียด
Martin Velez

1
แต่เราสามารถเพิ่มคอลัมน์ใหม่ได้
umesh


1
เป็นเรื่องแปลกที่คุณไม่สามารถเพิ่ม PK หลังจากสร้างตารางได้ แต่คุณสามารถเพิ่มดัชนี ( CREATE UNIQUE INDEX pkName ON tableName(columnName)) ได้เมื่อเฟรมเวิร์ก DB เช่น SMO ของ MS SQL ทำให้คุณเพิ่ม PK หลังจากสร้างตารางแล้ว!
SteveCinq

@deFreitas โปรดมอบภูมิปัญญาของคุณให้กับเรา เห็นได้ชัดว่าคุณต้องการให้คนอื่นรู้ว่าคุณไม่เห็นด้วยกับคำตอบหรือสิ่งที่ผู้แสดงความคิดเห็นคนหนึ่งกล่าวอย่างไรก็ตามความคิดเห็นของคุณไม่มีข้อมูลเลยนอกจากเจตนาที่ชัดเจนในการสื่อถึงความเหนือกว่าและความขี้ขลาด
Nathan Ridley

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 โดยตรง เคล็ดลับนี้ดูเหมือนจะใช้ได้ผล เป็นวิธีแก้ปัญหาแฮ็คแน่นอน

ในระยะสั้น: สร้างดัชนีปกติ (ไม่ซ้ำกัน) บนตารางจากนั้นทำให้สคีมาเขียนได้และเปลี่ยนชื่อของดัชนีเป็นแบบฟอร์มที่สงวนไว้โดย sqlite เพื่อระบุดัชนีคีย์หลัก (เช่น sqlite_autoindex_XXX_1 โดยที่ XXX คือชื่อตาราง) และตั้งค่าสตริง sql เป็น 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;

การทดสอบบางอย่าง (ใน 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 clause ในแบบสอบถามการอัปเดตครั้งที่สองโดยไม่ได้ตั้งใจ SQLite ไม่เป็นเช่นนั้น: P
Andrew Magee

7

ตามเอกสาร sqlite เกี่ยวกับการสร้างตารางการใช้create table as selectจะสร้างตารางใหม่โดยไม่มีข้อ จำกัด และไม่มีคีย์หลัก

อย่างไรก็ตามเอกสารประกอบยังระบุด้วยว่าคีย์หลักและดัชนีที่ไม่ซ้ำกันนั้นมีเหตุผลเทียบเท่ากัน ( ดูหัวข้อข้อ จำกัด ):

ในกรณีส่วนใหญ่ข้อ จำกัด UNIQUE และ PRIMARY KEY จะถูกนำมาใช้โดยการสร้างดัชนีเฉพาะในฐานข้อมูล (ข้อยกเว้นคือ INTEGER PRIMARY KEY และ PRIMARY KEY บนตารางที่ไม่มี ROWID) ดังนั้น schema ต่อไปนี้จึงเทียบเท่ากันทางตรรกะ:

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 คุณสามารถรับเอฟเฟกต์คีย์หลักเดียวกันได้โดยใช้ดัชนีเฉพาะ

นอกจากนี้ตารางใด ๆ (ยกเว้นตารางที่สร้างขึ้นโดยไม่มีไวยากรณ์ rowid) จะมีคอลัมน์จำนวนเต็มภายในที่เรียกว่า "rowid" ตามเอกสารคุณสามารถใช้คอลัมน์ด้านในนี้เพื่อดึง / แก้ไขตารางบันทึก


หากคุณใช้ EntityFramework เพื่อเชื่อมต่อกับฐานข้อมูลของคุณจะไม่รู้จักดัชนีที่ไม่ซ้ำกันเป็นคีย์หลัก ดังนั้นแม้ว่ามันจะเทียบเท่าในเชิงตรรกะและใช้งานได้ภายใน SQLite แต่ก็ไม่ได้เทียบเท่ากันทุกที่
Kristen Hammack


3

บทนำ

สิ่งนี้ขึ้นอยู่กับ java ของ Android และเป็นตัวอย่างที่ดีในการเปลี่ยนฐานข้อมูลโดยไม่ทำให้แฟน ๆ / ลูกค้าแอปพลิเคชันของคุณรำคาญ นี่เป็นไปตามแนวคิดของหน้าคำถามที่พบบ่อย SQLite http://sqlite.org/faq.html#q11

ปัญหา

ฉันไม่ได้สังเกตว่าฉันต้องตั้งค่า row_number หรือ record_id เพื่อลบสินค้าที่ซื้อรายการเดียวในใบเสร็จและในขณะเดียวกันหมายเลขบาร์โค้ดของสินค้าก็หลอกให้ฉันคิดว่าจะทำให้เป็นกุญแจสำคัญในการลบรายการนั้น ฉันกำลังบันทึกรายละเอียดใบเสร็จในรหัสใบเสร็จรับเงินในตาราง การทิ้งไว้โดยไม่มี record_id อาจหมายถึงการลบระเบียนทั้งหมดของรายการเดียวกันในใบเสร็จรับเงินหากฉันใช้บาร์โค้ดของรายการเป็นคีย์

ข้อสังเกต

โปรดเข้าใจว่านี่คือการคัดลอกวางรหัสของฉันที่ฉันกำลังดำเนินการอยู่ในขณะที่เขียนนี้ ใช้เป็นเพียงตัวอย่างเท่านั้นการคัดลอกวางแบบสุ่มจะไม่ช่วยคุณ แก้ไขสิ่งนี้ก่อนตามความต้องการของคุณ

นอกจากนี้โปรดอย่าลืมอ่านความคิดเห็นในโค้ด

รหัส

ใช้วิธีนี้เป็นวิธีการในชั้นเรียนของคุณเพื่อตรวจสอบที่ 1 ว่าคอลัมน์ที่คุณต้องการเพิ่มหายไปหรือไม่ เราทำสิ่งนี้เพื่อไม่ให้ทำซ้ำขั้นตอนการแก้ไขรหัสใบเสร็จรับเงินตาราง เพียงระบุว่าเป็นส่วนหนึ่งของชั้นเรียนของคุณ ในขั้นตอนต่อไปคุณจะเห็นว่าเราจะใช้มันอย่างไร

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

จากนั้นรหัสต่อไปนี้จะใช้ในการสร้างรหัสใบเสร็จรับเงินในตารางหากยังไม่ออกสำหรับผู้ใช้แอปของคุณเป็นครั้งแรก และโปรดสังเกต "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();
            }

1

ฉันมีปัญหาเดียวกันและทางออกที่ดีที่สุดที่ฉันพบคือสร้างตารางที่กำหนดคีย์หลักก่อนแล้วจึงใช้แทรกในคำสั่ง

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

INSERT INTO mytable 
SELECT field1, field2 
FROM anothertable;

ความคิดที่ไม่ดีสำหรับการแทรกจำนวนมาก
PirateApp

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

ใช้เครื่องมือเช่น DB Browser สำหรับ SQLite อนุญาตให้เพิ่ม PK, AI ได้โดยคลิกขวาที่ตาราง -> แก้ไข

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.