วิธีเพิ่มคอลัมน์ที่มีข้อ จำกัด foreign key ไปยังตารางที่มีอยู่แล้ว?


11

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

CREATE TABLE users (id int PRIMARY KEY);

-- already exists with data
CREATE TABLE message ();

ฉันจะเปลี่ยนmessagesตารางเช่นนั้นได้อย่างไร

  1. คอลัมน์ใหม่ที่เรียกว่าsenderถูกเพิ่มเข้าไป
  2. โดยที่senderเป็น foreign key อ้างอิงusersตาราง

สิ่งนี้ไม่ทำงาน

# ALTER TABLE message ADD FOREIGN KEY (sender) REFERENCES users;
ERROR:  column "sender" referenced in foreign key constraint does not exist

คำสั่งนี้ไม่ได้สร้างคอลัมน์ด้วยหรือไม่?


3
คุณต้องสร้างคอลัมน์ก่อนที่จะอ้างอิง ฉันจะลองอ่านเอกสารสำหรับALTER TABLEที่นี่และให้ความสนใจอย่างใกล้ชิดกับตัวอย่าง
Kassandry

ฮัสซันฉันทำความสะอาดคำถามนี้เพื่อใช้ DDL และฉันลบสิ่งที่ไม่ทำงาน ดูว่านี้สามารถตอบคำถาม: dba.stackexchange.com/a/202564/2639 อย่าลังเลที่จะปฏิเสธการแก้ไขใด ๆ เหล่านี้ฉันแค่อยากจะล้างสิ่งนี้เพื่อลูกหลาน
Evan Carroll

คำตอบ:


18

สิ่งที่ค่อนข้างง่าย - คุณเพียงแค่ต้องเพิ่มอีกขั้น

FOREIGN KEYคอลัมน์มีอยู่FKเพื่อที่จะทำให้มันเป็น ฉันทำสิ่งต่อไปนี้ (จากที่นี่และเอกสารประกอบ ):

CREATE TABLE x(t INT PRIMARY KEY);

CREATE TABLE y(s INT);

ALTER TABLE y ADD COLUMN z INT;    

ALTER TABLE y
  ADD CONSTRAINT y_x_fkey FOREIGN KEY (z)
      REFERENCES x (t)
      ON UPDATE CASCADE ON DELETE CASCADE;

ข้อควรทราบ:

ให้ชื่อที่มีความหมายกับคีย์ต่างประเทศของคุณเสมอ การได้รับแจ้งว่าคีย์ "SYS_C00308108" ถูกละเมิดไม่เป็นประโยชน์มาก ดูซอที่นี่สำหรับพฤติกรรมของ Oracle ภายใต้สถานการณ์เหล่านี้ชื่อคีย์จะแตกต่างกันไปจากซอถึงซอ แต่เป็นสตริงบางส่วนที่ขึ้นต้นด้วย SYS _... )

พิจารณาคำสั่งของคุณ:

ALTER TABLE message ADD FOREIGN KEY (sender) REFERENCES users;

มันจะเป็น "nice-to-have" ถ้า RDBMS สามารถสร้างเขตข้อมูลที่คุณต้องการโดยอัตโนมัติด้วยประเภทข้อมูลที่ตรงกับเขตข้อมูลอ้างอิง ทั้งหมดที่ฉันจะพูดคือการเปลี่ยนแปลง DDL นั้น (หรืออย่างน้อยก็ควร) การดำเนินการที่ไม่ค่อยได้ใช้และไม่ใช่สิ่งที่คุณต้องการทำอย่างสม่ำเสมอ นอกจากนี้ยังมีความเสี่ยงในการเพิ่มเอกสารที่มีสาระสำคัญอยู่แล้ว

อย่างน้อย PostgreSQL พยายามที่จะทำอะไรบางอย่างที่เหมาะสม - มันเชื่อมชื่อตารางที่FOREIGN KEYชื่อเขตและ_fkeyและแม้กระทั่งเพิ่มDETAIL: Key (sender_id)=(56) is not present in table "user_".เพื่อให้บางสิ่งบางอย่างที่อาจทำให้รู้สึกถึงความเป็นมนุษย์ - ดูซอนี่


2
ฉันไม่เคยตั้งชื่อกุญแจต่างประเทศของฉัน พวกเขาได้รับชื่อโดยอัตโนมัติและมักจะมีประโยชน์มาก "y_z_fkey"ยกตัวอย่างเช่นชื่อเริ่มต้นในบริบทที่เป็น ผมขอยืนยันว่าเป็นชื่อที่ดีกว่าy_x_fkeyเพราะการละเมิดของคุณไม่ได้บอกคุณคอลัมน์ที่คุณใส่กำลังเข้าไปในที่ที่ก่อให้เกิดข้อผิดพลาด ฉันไม่ค่อยสนใจว่าจะชี้ไปที่ใด ตามกฎทั่วไปคุณไม่ควรตั้งชื่อ fkeys ของคุณและให้ค่าเริ่มต้นของ PostgreSQL จัดการได้
Evan Carroll

นอกจากนี้คุณอาจไม่ต้องการแทนที่ค่าเริ่มต้นON UPDATE CASCADE ON DELETE CASCADE;ในตัวอย่างโดยเฉพาะอย่างยิ่งโดยไม่มีเหตุผล มันทำให้ตัวอย่างซับซ้อนและคุณไม่ต้องอธิบายว่ามันคืออะไร ฉันหนึ่งไม่ปกติต้องการลบไปน้ำตก
Evan Carroll

1
ฉันมักจะชื่อ FKs ตามการประชุมที่ บริษัท / โครงการได้ตัดสินใจไว้ มันไม่ได้เรื่องมากถ้ามันเป็นy_x_fkeyหรือy_z_fkeyหรือx__y_FKตราบเท่าที่มันมีความสอดคล้อง
ypercubeᵀᴹ

ฉันจะเห็นด้วยกับสิ่งนี้มากหากคุณทำสัญญา - เลือกการประชุมและทำตามและ / หรือตรวจสอบให้แน่ใจว่าคุณได้ปฏิบัติตามอนุสัญญาที่เคยใช้กับระบบมาก่อน
Vérace

@EvanCarroll - หากการประชุมของ PostgreSQL นั้นเป็นของโครงการหรือที่เคยตัดสินใจเกี่ยวกับระบบที่อาจไม่ใช่ PostgreSQL - ระบบอาจเริ่มต้นได้ดีเช่น Oracle หรือระบบอื่น ๆ ที่อาจไม่มีการประชุมของ PostgreSQL คุณสามารถยืนยันว่า x_y_z_fk อาจให้ข้อมูลที่เป็นไปได้สูงสุดในกรณีที่เกิดข้อผิดพลาด! เลือกบางสิ่งและยึดติดกับมันเป็นคติประจำใจของฉัน แต่อย่าให้ RDBMS หนึ่งอัน (ไม่ว่าจะดีแค่ไหน) ก็ตัดสินใจเลือกการประชุมให้คุณ!
Vérace

8

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

ALTER TABLE message
  ADD COLUMN sender INT
  REFERENCES users;  -- or REFERENCES table(unique_column)

จะทำงานได้ดี คุณสามารถดูไวยากรณ์ของALTER TABLEที่นี่

ALTER TABLE [ IF EXISTS ] [ ONLY ] name [ * ]
action [, ... ]

ด้วย"การกระทำ"เป็น

ADD [ COLUMN ] [ IF NOT EXISTS ] column_name data_type [ COLLATE collation ] [ column_constraint [ ... ] ]

ตัวอย่างเหล่านี้ยังอยู่ในเอกสาร

ALTER TABLE distributors
  ADD CONSTRAINT distfk
  FOREIGN KEY (address)
  REFERENCES addresses (address);

ALTER TABLE distributors
  ADD CONSTRAINT distfk
  FOREIGN KEY (address)
  REFERENCES addresses (address)
  NOT VALID;

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


0

กรณีที่ 1 : ถ้าคุณต้องการสร้าง foreign key ในขณะที่สร้างตารางใหม่

CREATE TABLE table1(
id SERIAL PRIMARY KEY,
column1 varchar(n) NOT NULL,
table2_id SMALLINT REFERENCES table2(id)
); 

คำสั่งข้างต้นจะสร้างตารางที่มีชื่อ 'table1' และสามคอลัมน์ชื่อ 'id' (คีย์หลัก), 'column1', 'table2_id' (foreign key ของ table1 ที่อ้างอิงคอลัมน์ id ของ table2)

DATATYPE 'serial' จะทำให้คอลัมน์ที่ใช้ประเภทข้อมูลนี้เป็นคอลัมน์ที่สร้างขึ้นอัตโนมัติเมื่อแทรกค่าลงในตารางที่คุณไม่จำเป็นต้องพูดถึงคอลัมน์นี้เลยหรือคุณสามารถให้ 'เริ่มต้น' โดยไม่ต้องใส่เครื่องหมายอัญประกาศที่ตำแหน่งค่า

คอลัมน์คีย์หลักจะถูกเพิ่มเข้าไปในดัชนีของตารางที่มีค่า 'tablename_pkey' เสมอ

หากมีการเพิ่มรหัสต่างประเทศในเวลาที่สร้างตาราง CONSTRAINT จะถูกเพิ่มด้วยรูปแบบ '(ปัจจุบัน _table_name) _ (foreign_key_id_name) _fkey'

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

กรณีที่ 2: ถ้าคุณต้องการ foreign key ไปยังตารางที่มีอยู่ในคอลัมน์ที่มีอยู่

ALTER TABLE table1
ADD CONSTRAINT table1_table2_id_id_fkey
FOREIGN KEY (table2_id) REFERENCES table2(id);

หมายเหตุ: วงเล็บ '()' หลังจากคีย์ต่างประเทศและการอ้างอิง tabel2 เป็นภาคบังคับมิฉะนั้น postgres จะเกิดข้อผิดพลาด


0

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


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