Làm cách nào để thêm khóa ngoại vào bảng SQLite hiện có?


128

Tôi có bảng sau:

CREATE TABLE child( 
  id INTEGER PRIMARY KEY, 
  parent_id INTEGER, 
  description TEXT);

Làm thế nào để tôi thêm một ràng buộc khóa ngoại parent_id? Giả sử khóa ngoại được bật.

Hầu hết các ví dụ cho rằng bạn đang tạo bảng - Tôi muốn thêm ràng buộc vào bảng hiện có.


Lệnh SQLite ALTER chỉ hỗ trợ "đổi tên bảng" và "thêm cột". Tuy nhiên, chúng ta có thể thực hiện các thay đổi tùy ý khác đối với định dạng của bảng bằng cách sử dụng một chuỗi các thao tác đơn giản. Kiểm tra câu trả lời của tôi
situee

Câu trả lời:


198

Bạn không thể.

Mặc dù cú pháp SQL-92 để thêm khóa ngoại vào bảng của bạn sẽ như sau:

ALTER TABLE child ADD CONSTRAINT fk_child_parent
                  FOREIGN KEY (parent_id) 
                  REFERENCES parent(id);

SQLite không hỗ trợ các ADD CONSTRAINTbiến thể của ALTER TABLElệnh ( sqlite.org: Tính năng SQL Đó SQLite không thực hiện ).

Do đó, cách duy nhất để thêm khóa ngoại trong sqlite 3.6.1 là trong thời gian CREATE TABLEnhư sau:

CREATE TABLE child ( 
    id           INTEGER PRIMARY KEY, 
    parent_id    INTEGER, 
    description  TEXT,
    FOREIGN KEY (parent_id) REFERENCES parent(id)
);

Thật không may, bạn sẽ phải lưu dữ liệu hiện có vào một bảng tạm thời, bỏ bảng cũ, tạo bảng mới với ràng buộc FK, sau đó sao chép dữ liệu trở lại từ bảng tạm thời. ( sqlite.org - Câu hỏi thường gặp: Q11 )


28
Tôi nghĩ việc đổi tên bảng cũ sẽ dễ dàng hơn, tạo bảng mới và sao chép dữ liệu trở lại. Sau đó, bạn có thể thả bảng cũ.
tuinstoel

Vâng, đó là dễ dàng hơn. Tôi chỉ trích dẫn Câu hỏi thường gặp về sqlite: sqlite.org/faq.html#q11 . Trên thực tế, RENAME TOlà một trong số ít ALTER TABLEcác biến thể hiện được hỗ trợ trong sqlite 3.
Daniel Vassallo

3
Không nên là: FOREIGN KEY (Parent_id) TÀI LIỆU THAM KHẢO Cha mẹ (id) Đúng, Jonathan đã không đưa ra tên của "bảng cha". Trên thực tế, bảng nên được đặt tên người, nhưng ...
igoroidi

3
Đây dường như là một vấn đề lớn đối với tôi. Thông thường khi bạn kết xuất cơ sở dữ liệu, trước tiên bạn xuất các lệnh CREATE TABLE. Sau đó, XÓA các lệnh INTO và cuối cùng là THÊM các lệnh CONSTRAINT. Nếu có sự phụ thuộc vòng tròn (giá trị khóa ngoài) trong dữ liệu của bạn, thì bạn không thể chèn dữ liệu của mình trong khi khóa ngoại được thi hành. Nhưng nếu bạn không thể thêm các ràng buộc khóa ngoại sau đó, thì bạn bị mắc kẹt. Tất nhiên có những hạn chế hoãn lại, nhưng điều này rất vụng về.
nagylzs

9
KHÔNG đổi tên bảng cũ như đã nói trong nhận xét đầu tiên nếu các bảng khác có tham chiếu đến bảng này! Trong trường hợp này, bạn cũng sẽ phải tạo lại tất cả các bảng này.
rocknow

57

Bạn có thể thêm ràng buộc nếu bạn thay đổi bảng và thêm cột sử dụng ràng buộc.

Đầu tiên, tạo bảng mà không có Parent_id:

CREATE TABLE child( 
  id INTEGER PRIMARY KEY,  
  description TEXT);

Sau đó, thay đổi bảng:

ALTER TABLE child ADD COLUMN parent_id INTEGER REFERENCES parent(id);

2
Tốt để làm quen với trình tự này, nhưng điều này không trả lời câu hỏi thực tế: Tôi muốn thêm các ràng buộc cho một trình tự hiện có.
Sói

9

Vui lòng kiểm tra https://www.sqlite.org/lang_altertable.html#otheralter

Các lệnh thay đổi lược đồ duy nhất được SQLite hỗ trợ trực tiếp là các lệnh "đổi tên bảng" và "thêm cột" được hiển thị ở trên. Tuy nhiên, các ứng dụng có thể thực hiện các thay đổi tùy ý khác đối với định dạng của bảng bằng cách sử dụng một chuỗi các thao tác đơn giản. Các bước để thực hiện các thay đổi tùy ý đối với thiết kế lược đồ của một số bảng X như sau:

  1. Nếu các ràng buộc khóa ngoại được bật, hãy vô hiệu hóa chúng bằng PRAGMA Foreign_keys = OFF.
  2. Bắt đầu một giao dịch.
  3. Ghi nhớ định dạng của tất cả các chỉ mục và trình kích hoạt được liên kết với bảng X. Thông tin này sẽ cần thiết trong bước 8 bên dưới. Một cách để làm điều này là chạy một truy vấn như sau: Kiểu CHỌN, sql TỪ sqlite_master WHERE tbl_name = 'X'.
  4. Sử dụng CREATE TABLE để xây dựng một bảng mới "new_X" có định dạng sửa đổi mong muốn của bảng X. Tất nhiên, hãy đảm bảo rằng tên "new_X" không va chạm với bất kỳ tên bảng hiện có nào.
  5. Chuyển nội dung từ X sang new_X bằng cách sử dụng một câu lệnh như: INSERT INTO new_X CHỌN ... TỪ X.
  6. Bỏ bảng cũ X: DROP TABLE X.
  7. Thay đổi tên của new_X thành X bằng cách sử dụng: ALTER TABLE new_X RENAME TO X.
  8. Sử dụng CREATE INDEX và CREATE TRIGGER để xây dựng lại các chỉ mục và trình kích hoạt được liên kết với bảng X. Có lẽ sử dụng định dạng cũ của trình kích hoạt và chỉ mục được lưu từ bước 3 ở trên làm hướng dẫn, thay đổi khi thích hợp cho việc thay đổi.
  9. Nếu bất kỳ khung nhìn nào tham chiếu đến bảng X theo cách bị ảnh hưởng bởi thay đổi lược đồ, thì hãy bỏ các khung nhìn đó bằng DROP VIEW và tạo lại chúng với bất kỳ thay đổi nào là cần thiết để điều chỉnh thay đổi lược đồ bằng CREATE VIEW.
  10. Nếu các ràng buộc khóa ngoài ban đầu được kích hoạt thì hãy chạy PRAGMA Foreign_key_check để xác minh rằng thay đổi lược đồ không phá vỡ bất kỳ ràng buộc khóa ngoại nào.
  11. Cam kết giao dịch bắt đầu ở bước 2.
  12. Nếu các ràng buộc khóa ngoại ban đầu được kích hoạt, hãy kích hoạt chúng ngay bây giờ.

Quy trình trên hoàn toàn chung chung và sẽ hoạt động ngay cả khi thay đổi lược đồ khiến thông tin được lưu trữ trong bảng thay đổi. Vì vậy, quy trình đầy đủ ở trên phù hợp để bỏ một cột, thay đổi thứ tự các cột, thêm hoặc xóa ràng buộc UNIITE hoặc PRIMARY KEY, thêm ràng buộc CHECK hoặc FOREIGN KEY hoặc KHÔNG NULL hoặc thay đổi kiểu dữ liệu cho cột.


4

Có, bạn có thể, mà không cần thêm một cột mới. Bạn phải cẩn thận để làm điều đó một cách chính xác để tránh làm hỏng cơ sở dữ liệu, vì vậy bạn nên sao lưu hoàn toàn cơ sở dữ liệu của mình trước khi thử điều này.

cho ví dụ cụ thể của bạn:

CREATE TABLE child(
  id INTEGER PRIMARY KEY,
  parent_id INTEGER,
  description TEXT
);

--- create the table we want to reference
create table parent(id integer not null primary key);

--- now we add the foreign key
pragma writable_schema=1;
update SQLITE_MASTER set sql = replace(sql, 'description TEXT)',
    'description TEXT, foreign key (parent_id) references parent(id))'
) where name = 'child' and type = 'table';

--- test the foreign key
pragma foreign_keys=on;
insert into parent values(1);
insert into child values(1, 1, 'hi'); --- works
insert into child values(2, 2, 'bye'); --- fails, foreign key violation

hay nói chung hơn:

pragma writable_schema=1;

// replace the entire table's SQL definition, where new_sql_definition contains the foreign key clause you want to add
UPDATE SQLITE_MASTER SET SQL = new_sql_definition where name = 'child' and type = 'table';

// alternatively, you might find it easier to use replace, if you can match the exact end of the sql definition
// for example, if the last column was my_last_column integer not null:
UPDATE SQLITE_MASTER SET SQL = replace(sql, 'my_last_column integer not null', 'my_last_column integer not null, foreign key (col1, col2) references other_table(col1, col2)') where name = 'child' and type = 'table';

pragma writable_schema=0;

Dù bằng cách nào, trước tiên bạn có thể muốn xem định nghĩa SQL là gì trước khi bạn thực hiện bất kỳ thay đổi nào:

select sql from SQLITE_MASTER where name = 'child' and type = 'table';

Nếu bạn sử dụng phương pháp thay thế (), bạn có thể thấy nó hữu ích, trước khi thực hiện, trước tiên hãy kiểm tra lệnh thay thế () của bạn bằng cách chạy:

select replace(sql, ...) from SQLITE_MASTER where name = 'child' and type = 'table';

3

Nếu bạn đang sử dụng trình quản lý sqlite bổ trợ của Firefox, bạn có thể làm như sau:

Thay vì thả và tạo lại bảng, người ta có thể sửa đổi nó như thế này.

Trong hộp văn bản Cột, nhấp chuột phải vào tên cột cuối cùng được liệt kê để hiển thị menu ngữ cảnh và chọn Chỉnh sửa Cột. Lưu ý rằng nếu cột cuối cùng trong định nghĩa BẢNG là KHÓA CHÍNH thì trước tiên cần thêm một cột mới và sau đó chỉnh sửa loại cột của cột mới để thêm định nghĩa FOREIGN KEY. Trong hộp Kiểu cột, nối thêm dấu phẩy và dấu

FOREIGN KEY (parent_id) REFERENCES parent(id)

định nghĩa sau kiểu dữ liệu. Nhấp vào nút Thay đổi và sau đó nhấp vào nút Có trên hộp thoại Hoạt động Nguy hiểm.

Tham khảo: Trình quản lý Sqlite


2

Bạn có thể thử điều này:

ALTER TABLE [Child] ADD COLUMN column_name INTEGER REFERENCES parent_table_name(column_id);

-1

Về cơ bản bạn không thể nhưng bạn có thể bỏ qua tình huống.

Cách chính xác để thêm ràng buộc khóa ngoại vào bảng hiện có là lệnh sau.

db.execSQL("alter table child add column newCol integer REFERENCES parent(parent_Id)");

Sau đó sao chép dữ liệu Parent_Id vào newCol và sau đó xóa cột Parent_Id . Do đó, không cần bảng tạm thời.


Có vẻ như bạn đã không đọc kỹ câu hỏi. Vấn đề là chỉ thêm một ràng buộc nước ngoài, không thêm một cột có ràng buộc.
Sói

Không. Nó không trả lời câu hỏi.
MK

-4

Đầu tiên thêm một cột trong bảng con Cidnhư intsau đó alter tablevới mã dưới đây. Bằng cách này, bạn có thể thêm khóa ngoại Cidlàm khóa chính của bảng cha và sử dụng nó làm khóa ngoại trong bảng con ... hy vọng nó sẽ giúp bạn vì nó tốt cho tôi:

ALTER TABLE [child] 
  ADD CONSTRAINT [CId] 
  FOREIGN KEY ([CId]) 
  REFERENCES [Parent]([CId]) 
  ON DELETE CASCADE ON UPDATE NO ACTION;
GO

1
Điều này không hợp lệ trong SQLite. Ngoài ra đây là cú pháp MS SQL.
StilesCrisis
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.