SQLite thêm khóa chính


99

Tôi đã tạo một bảng trong Sqlite bằng cách sử dụng CREATE TABLE AScú pháp để tạo bảng dựa trênSELECT câu lệnh. Bây giờ bảng này không có khóa chính nhưng tôi muốn thêm một khóa.

Việc thực thi gây ALTER TABLE table_name ADD PRIMARY KEY(col1, col2,...)ra lỗi cú pháp "gần CHÍNH XÁC"

Có cách nào để thêm khóa chính trong khi tạo bảng hoặc sau đó trong Sqlite không?

Bởi "trong quá trình sáng tạo", ý tôi là trong quá trình sáng tạo với CREATE TABLE AS.


1
bạn có thể sử dụng bất kỳ trình duyệt db nào để chỉnh sửa cơ sở dữ liệu. Họ cũng đang xóa và tạo các bảng. nhưng chúng tôi không muốn bận tâm về nó. bạn có thể tải trình duyệt db cho bất kỳ hệ điều hành từ đây sqlitebrowser.org
vichu

Câu trả lời:


121

Bạn không thể sửa đổi các bảng SQLite theo bất kỳ cách quan trọng nào sau khi chúng đã được tạo. Giải pháp đề xuất được chấp nhận là tạo một bảng mới với các yêu cầu chính xác và sao chép dữ liệu của bạn vào đó, sau đó bỏ bảng cũ.

đây là tài liệu chính thức về điều này: http://sqlite.org/faq.html#q11


6
Liên kết này ( sqlite.org/omitted.html ) giải thích chi tiết hơn những gì đã bị bỏ qua.
Martin Velez

1
nhưng chúng ta có thể thêm các cột mới
Umesh


1
Thật kỳ lạ khi bạn không thể thêm PK sau khi tạo bảng nhưng bạn có thể thêm chỉ mục ( CREATE UNIQUE INDEX pkName ON tableName(columnName)) khi các khung công tác DB như SMO của MS SQL thực sự khiến bạn thêm PK sau khi bảng đã được tạo!
SteveCinq,

@deFreitas Vui lòng ban sự thông thái của bạn cho chúng tôi. Rõ ràng là bạn muốn mọi người biết bạn không chấp nhận câu trả lời hoặc điều gì đó mà một trong những người bình luận đã nói, tuy nhiên bình luận của bạn không chứa thông tin gì cả, bên cạnh mục đích rõ ràng là truyền đạt sự vượt trội và phản cảm.
Nathan Ridley

31

Miễn là bạn đang sử dụng CREATE TABLE, nếu bạn đang tạo khóa chính trên một trường , bạn có thể sử dụng:

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

Với CREATE TABLE, bạn cũng có thể luôn sử dụng phương pháp sau để tạo khóa chính trên một hoặc nhiều trường :

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

Tham khảo: http://www.sqlite.org/lang_createtable.html

Câu trả lời này không giải quyết việc thay đổi bảng.


10

Tôi đã cố gắng thêm khóa chính sau đó bằng cách thay đổi trực tiếp bảng sqlite_master. Thủ thuật này có vẻ hiệu quả. Đó là một giải pháp hack tất nhiên.

Tóm lại: tạo một chỉ mục thông thường (duy nhất) trên bảng, sau đó làm cho lược đồ có thể ghi được và thay đổi tên của chỉ mục thành dạng do sqlite dành riêng để xác định chỉ mục khóa chính, (tức là sqlite_autoindex_XXX_1, trong đó XXX là tên bảng) và đặt chuỗi sql thành NULL. Cuối cùng thay đổi định nghĩa bảng chính nó. Một pittfal: sqlite không thấy tên chỉ mục thay đổi cho đến khi cơ sở dữ liệu được mở lại. Đây có vẻ giống như một lỗi, nhưng không phải là một lỗi nghiêm trọng (ngay cả khi không mở lại cơ sở dữ liệu, bạn vẫn có thể sử dụng nó).

Giả sử bảng trông như sau:

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

Sau đó, tôi đã làm như sau:

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;

Một số thử nghiệm (trong 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
Chỉ cần một cảnh báo rằng bạn có thể (theo như tôi có thể nói) khiến toàn bộ cơ sở dữ liệu của bạn không thể truy cập được nếu bạn làm sai điều này. Tôi đang chơi xung quanh và tôi đã vô tình bỏ lỡ mệnh đề WHERE trong truy vấn cập nhật thứ hai. SQLite không như thế: P
Andrew Magee

7

Theo tài liệu sqlite về tạo bảng, việc sử dụng bảng tạo khi chọn sẽ tạo ra một bảng mới không có ràng buộc và không có khóa chính.

Tuy nhiên, tài liệu cũng nói rằng các khóa chính và chỉ mục duy nhất là tương đương về mặt logic ( xem phần ràng buộc ):

Trong hầu hết các trường hợp, các ràng buộc UNIQUE và PRIMARY KEY được thực hiện bằng cách tạo một chỉ mục duy nhất trong cơ sở dữ liệu. (Các trường hợp ngoại lệ là INTEGER PRIMARY KEY và PRIMARY KEY trên các bảng KHÔNG ROWID.) Do đó, các lược đồ sau tương đương về mặt logic:

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

Vì vậy, ngay cả khi bạn không thể thay đổi định nghĩa bảng của mình thông qua cú pháp thay đổi SQL, bạn có thể có được hiệu ứng khóa chính tương tự thông qua việc sử dụng một chỉ mục duy nhất.

Ngoài ra, bất kỳ bảng nào (ngoại trừ những bảng được tạo không có cú pháp rowid) có một cột số nguyên bên trong được gọi là "rowid". Theo tài liệu, bạn có thể sử dụng cột bên trong này để truy xuất / sửa đổi bảng ghi.


Nếu bạn đang sử dụng EntityFramework để kết nối với cơ sở dữ liệu của mình, nó không nhận ra một chỉ mục duy nhất làm khóa chính. Vì vậy, mặc dù nó tương đương về mặt logic và chức năng bên trong SQLite, nhưng nó không hoàn toàn tương đương ở mọi nơi.
Kristen Hammack

5

Bạn có thể làm như thế này:

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

3

Giới thiệu

Điều này dựa trên java của Android và đó là một ví dụ điển hình về việc thay đổi cơ sở dữ liệu mà không làm phiền người hâm mộ / khách hàng ứng dụng của bạn. Điều này dựa trên ý tưởng của trang Câu hỏi thường gặp về SQLite http://sqlite.org/faq.html#q11

Vấn đề

Tôi không nhận thấy rằng tôi cần phải đặt row_number hoặc record_id để xóa một mặt hàng đã mua trong biên lai và đồng thời số mã vạch của mặt hàng đã đánh lừa tôi nghĩ rằng nó là chìa khóa để xóa mặt hàng đó. Tôi đang lưu chi tiết biên nhận trong bảng biên nhận_barcode. Để nó không có record_id có thể có nghĩa là xóa tất cả các bản ghi của cùng một mặt hàng trong biên lai nếu tôi sử dụng mã vạch mặt hàng làm khóa.

Để ý

Vui lòng hiểu rằng đây là bản sao-dán mã của tôi mà tôi đang thực hiện tại thời điểm viết bài này. Chỉ sử dụng nó làm ví dụ, sao chép dán ngẫu nhiên sẽ không giúp ích cho bạn. Sửa đổi điều này trước theo nhu cầu của bạn

Cũng xin đừng quên đọc các bình luận trong mã.

Mật mã

Sử dụng phương thức này như một phương thức trong lớp của bạn để kiểm tra thứ nhất xem cột bạn muốn thêm có bị thiếu hay không. Chúng tôi làm điều này chỉ để không lặp lại quá trình thay đổi bảng biên nhận_barcode. Chỉ cần đề cập đến nó như một phần của lớp học của bạn. Trong bước tiếp theo, bạn sẽ thấy cách chúng tôi sử dụng nó.

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

Sau đó, mã sau đây được sử dụng để tạo bảng biên nhận_barcode nếu nó KHÔNG thoát cho người dùng lần đầu tiên sử dụng ứng dụng của bạn. Và hãy lưu ý "NẾU KHÔNG TỒN TẠI" trong mã. Nó có tầm quan trọng.

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

Tôi đã gặp vấn đề tương tự và giải pháp tốt nhất mà tôi tìm thấy là trước tiên tạo bảng xác định khóa chính và sau đó sử dụng chèn vào câu lệnh.

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

INSERT INTO mytable 
SELECT field1, field2 
FROM anothertable;

ý tưởng tồi cho chèn hàng loạt
PirateApp

0

Tôi đã sử dụng cú pháp CREATE TABLE AS để hợp nhất một số cột và gặp phải vấn đề tương tự. Đây là một AppleScript tôi đã viết để tăng tốc quá trình.

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

Tôi nghĩ rằng thêm một chỉ mục trên cột đó có thể có được hiệu quả tương tự.


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

Sử dụng công cụ như DB Browser cho SQLite, nó cho phép thêm PK, AI bằng cách nhấp chuột phải đơn giản vào bảng -> sửa đổi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.