ฉันจะเพิ่มคีย์ภายนอกลงในตาราง SQLite ที่มีอยู่ได้อย่างไร


128

ฉันมีตารางต่อไปนี้:

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

ฉันจะเพิ่มข้อ จำกัด ของ Foreign Key ได้parent_idอย่างไร? สมมติว่ามีการเปิดใช้งานคีย์ต่างประเทศ

ตัวอย่างส่วนใหญ่สมมติว่าคุณกำลังสร้างตาราง - ฉันต้องการเพิ่มข้อ จำกัด ให้กับตารางที่มีอยู่


คำสั่ง SQLite ALTER รองรับเฉพาะ "เปลี่ยนชื่อตาราง" และ "เพิ่มคอลัมน์" อย่างไรก็ตามเราสามารถเปลี่ยนแปลงรูปแบบของตารางโดยพลการโดยใช้ลำดับขั้นตอนง่ายๆ ตรวจสอบคำตอบของฉัน
situee

คำตอบ:


198

คุณทำไม่ได้

แม้ว่าไวยากรณ์ SQL-92 ในการเพิ่มคีย์นอกลงในตารางของคุณจะเป็นดังนี้:

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

SQLite ไม่สนับสนุนADD CONSTRAINTแตกต่างจากALTER TABLEคำสั่ง ( sqlite.org: SQL คุณสมบัติที่ SQLite ไม่ใช้ )

ดังนั้นวิธีเดียวที่จะเพิ่มคีย์ต่างประเทศใน sqlite 3.6.1 มีCREATE TABLEดังนี้:

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

น่าเสียดายที่คุณจะต้องบันทึกข้อมูลที่มีอยู่ลงในตารางชั่วคราวปล่อยตารางเก่าสร้างตารางใหม่ด้วยข้อ จำกัด FK จากนั้นคัดลอกข้อมูลกลับเข้ามาจากตารางชั่วคราว ( sqlite.org - คำถามที่พบบ่อย: Q11 )


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

ใช่ว่าจะง่ายกว่า ผมก็แค่อ้างคำถามที่พบบ่อย SQLite: sqlite.org/faq.html#q11 ในความเป็นจริงRENAME TOเป็นหนึ่งในไม่กี่ALTER TABLEตัวแปรที่รองรับใน sqlite 3
Daniel Vassallo

3
ไม่ควรเป็น: FOREIGN KEY (parent_id) REFERENCES parent (id) จริงโจนาธานไม่ได้ให้ชื่อ "ตารางหลัก" อันที่จริงโต๊ะควรตั้งชื่อบุคคล แต่ ...
igorludi

3
นี่ดูเหมือนจะเป็นปัญหาใหญ่สำหรับฉัน โดยปกติเมื่อคุณถ่ายโอนฐานข้อมูลคุณจะส่งออกคำสั่ง CREATE TABLE ก่อน จากนั้น INSERT INTO คำสั่งและสุดท้ายเพิ่มคำสั่ง CONSTRAINT หากมีการอ้างอิงแบบวงกลม (ค่าคีย์ต่างประเทศ) ในข้อมูลของคุณคุณจะไม่สามารถแทรกข้อมูลของคุณได้ในขณะที่มีการบังคับใช้คีย์ต่างประเทศ แต่ถ้าคุณไม่สามารถเพิ่มข้อ จำกัด ของ Foreign Key ได้ในภายหลังแสดงว่าคุณติดขัด แน่นอนว่ามีข้อ จำกัด รอการตัดบัญชี แต่นี่เป็นเรื่องที่เงอะงะมาก
nagylzs

9
อย่าเปลี่ยนชื่อโต๊ะเก่าตามที่กล่าวไว้ในความเห็นแรกหากตารางอื่นมีการอ้างอิงถึงตารางนี้! ในกรณีนี้คุณจะต้องสร้างตารางนี้ใหม่ด้วย
rocknow

57

คุณสามารถเพิ่มข้อ จำกัด ได้หากคุณแก้ไขตารางและเพิ่มคอลัมน์ที่ใช้ข้อ จำกัด

ขั้นแรกสร้างตารางโดยไม่มี parent_id:

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

จากนั้นแก้ไขตาราง:

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

2
คุ้นเคยกับลำดับนี้ดี แต่สิ่งนี้ไม่ตอบคำถามจริง: ฉันต้องการเพิ่มข้อ จำกัด ให้กับข้อ จำกัด ที่มีอยู่
Wolf

10

โปรดตรวจสอบhttps://www.sqlite.org/lang_altertable.html#otheralter

คำสั่งการแก้ไขสคีมาเดียวที่ SQLite สนับสนุนโดยตรงคือคำสั่ง "เปลี่ยนชื่อตาราง" และ "เพิ่มคอลัมน์" ที่แสดงด้านบน อย่างไรก็ตามแอปพลิเคชันสามารถเปลี่ยนแปลงรูปแบบของตารางโดยพลการโดยใช้ลำดับขั้นตอนง่ายๆ ขั้นตอนในการเปลี่ยนแปลงโดยพลการในการออกแบบสคีมาของตาราง X บางตารางมีดังนี้:

  1. หากเปิดใช้งานข้อ จำกัด ของ Foreign Key ให้ปิดใช้งานโดยใช้ PRAGMA Foreign_keys = OFF
  2. เริ่มการทำธุรกรรม
  3. จำรูปแบบของดัชนีและทริกเกอร์ทั้งหมดที่เกี่ยวข้องกับตาราง X ข้อมูลนี้จำเป็นต้องใช้ในขั้นตอนที่ 8 ด้านล่าง วิธีหนึ่งในการดำเนินการนี้คือการเรียกใช้แบบสอบถามดังต่อไปนี้: ประเภท SELECT, sql จาก sqlite_master WHERE tbl_name = 'X'
  4. ใช้ CREATE TABLE เพื่อสร้างตารางใหม่ "new_X" ซึ่งอยู่ในรูปแบบตาราง X ที่แก้ไขแล้วตามต้องการตรวจสอบให้แน่ใจว่าชื่อ "new_X" ไม่ชนกับชื่อตารางที่มีอยู่แน่นอน
  5. ถ่ายโอนเนื้อหาจาก X ไปยัง new_X โดยใช้คำสั่งเช่น: INSERT INTO new_X SELECT ... FROM X.
  6. วางตารางเก่า X: DROP TABLE X
  7. เปลี่ยนชื่อ new_X เป็น X โดยใช้: ALTER TABLE new_X เปลี่ยนชื่อเป็น X
  8. ใช้ CREATE INDEX และ CREATE TRIGGER เพื่อสร้างดัชนีและทริกเกอร์ใหม่ที่เกี่ยวข้องกับตาราง X บางทีอาจใช้รูปแบบเก่าของทริกเกอร์และดัชนีที่บันทึกไว้จากขั้นตอนที่ 3 ข้างต้นเป็นแนวทางทำการเปลี่ยนแปลงตามความเหมาะสมสำหรับการเปลี่ยนแปลง
  9. หากมุมมองใด ๆ อ้างถึงตาราง X ในลักษณะที่ได้รับผลกระทบจากการเปลี่ยนแปลงสคีมาให้ทิ้งมุมมองเหล่านั้นโดยใช้ DROP VIEW และสร้างขึ้นใหม่พร้อมกับการเปลี่ยนแปลงที่จำเป็นเพื่อรองรับการเปลี่ยนแปลงสคีมาโดยใช้ CREATE VIEW
  10. หากเปิดใช้งานข้อ จำกัด ของคีย์ต่างประเทศในตอนแรกให้รัน PRAGMA Foreign_key_check เพื่อตรวจสอบว่าการเปลี่ยนแปลงสคีมาไม่ได้ทำลายข้อ จำกัด ของคีย์ต่างประเทศ
  11. เริ่มการทำธุรกรรมในขั้นตอนที่ 2
  12. หากเดิมเปิดใช้งานข้อ จำกัด ของคีย์ต่างประเทศให้เปิดใช้ใหม่ทันที

ขั้นตอนข้างต้นเป็นขั้นตอนทั่วไปอย่างสมบูรณ์และจะใช้งานได้แม้ว่าการเปลี่ยนแปลงสคีมาจะทำให้ข้อมูลที่จัดเก็บในตารางเปลี่ยนไป ดังนั้นขั้นตอนทั้งหมดข้างต้นจึงเหมาะสมสำหรับการวางคอลัมน์เปลี่ยนลำดับของคอลัมน์เพิ่มหรือลบข้อ จำกัด UNIQUE หรือคีย์หลักเพิ่ม CHECK หรือ FOREIGN KEY หรือ NOT NULL หรือเปลี่ยนประเภทข้อมูลสำหรับคอลัมน์เป็นต้น


4

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

สำหรับตัวอย่างเฉพาะของคุณ:

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

หรือมากกว่าโดยทั่วไป:

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;

ไม่ว่าจะด้วยวิธีใดคุณอาจต้องการทราบก่อนว่านิยาม SQL คืออะไรก่อนที่คุณจะทำการเปลี่ยนแปลงใด ๆ :

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

หากคุณใช้วิธีการแทนที่ () คุณอาจพบว่ามีประโยชน์ก่อนที่จะดำเนินการให้ทดสอบคำสั่งแทนที่ () ของคุณก่อนโดยรัน:

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

3

หากคุณใช้โปรแกรมเสริม sqlite-manager ของ Firefox คุณสามารถทำสิ่งต่อไปนี้:

แทนที่จะวางและสร้างตารางอีกครั้งเราสามารถปรับเปลี่ยนได้เช่นนี้

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

FOREIGN KEY (parent_id) REFERENCES parent(id)

นิยามหลังชนิดข้อมูล คลิกที่ปุ่ม Change จากนั้นคลิกปุ่ม Yes บนกล่องโต้ตอบ Dangerous Operation

ข้อมูลอ้างอิง: Sqlite Manager



-1

โดยพื้นฐานแล้วคุณทำไม่ได้ แต่คุณสามารถข้ามสถานการณ์ได้

วิธีที่ถูกต้องในการเพิ่มข้อ จำกัด ของ Foreign Key ในตารางที่มีอยู่คือคำสั่งต่อไปนี้

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

จากนั้นคัดลอกข้อมูลparent_IdไปยังnewColจากนั้นลบคอลัมน์Parent_Id ดังนั้นไม่จำเป็นต้องมีโต๊ะชั่วคราว


ดูเหมือนว่าคุณไม่ได้อ่านคำถามอย่างละเอียด ปัญหาคือการเพิ่มข้อ จำกัด แปลกปลอมเท่านั้นไม่ใช่การเพิ่มคอลัมน์ที่มีข้อ จำกัด
Wolf

Nope มันไม่ตอบคำถามที่ถาม
MK

-4

แรกเพิ่มคอลัมน์ในตารางเด็กCidเป็นintแล้วalter tableด้วยโค้ดด้านล่าง ด้วยวิธีนี้คุณสามารถเพิ่มคีย์ต่างประเทศCidเป็นคีย์หลักของตารางหลักและใช้เป็นคีย์ต่างประเทศในตารางลูก ... หวังว่ามันจะช่วยคุณได้เพราะมันดีสำหรับฉัน:

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

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