Bagaimana cara menambahkan kolom dengan batasan kunci asing ke tabel yang sudah ada?

11

Saya punya tabel berikut,

CREATE TABLE users (id int PRIMARY KEY);

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

Bagaimana cara mengubah messagestabel sedemikian rupa sehingga,

  1. kolom baru yang disebut senderditambahkan ke dalamnya
  2. di mana senderkunci asing merujuk userstabel

Ini tidak berhasil

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

Apakah pernyataan ini tidak membuat kolom juga?

Hassan Baig
sumber
3
Anda perlu membuat kolom sebelum referensi. Saya juga akan mencoba membaca dokumentasi untuk ALTER TABLE di sini, dan memperhatikan contoh-contohnya.
Kassandry
Hassan, saya membersihkan pertanyaan ini untuk menggunakan DDL dan saya menghapus hal-hal yang tidak berfungsi. Lihat apakah ini menjawab pertanyaan: dba.stackexchange.com/a/202564/2639 . Merasa bebas untuk menolak salah satu dari suntingan ini, saya hanya ingin membersihkan ini untuk anak cucu.
Evan Carroll
@Kassandry dba.stackexchange.com/a/202564/2639
Evan Carroll

Jawaban:

18

Apa yang relatif mudah - Anda hanya perlu menambahkan langkah lain.

The FOREIGN KEYKolom harus ada untuk menjadikannya sebuah FK. Saya melakukan yang berikut (dari sini dan dokumentasinya ):

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;

Beberapa hal yang perlu diperhatikan:

SELALU memberikan kunci asing Anda nama yang berarti. Diberitahu bahwa kunci "SYS_C00308108" dilanggar tidak terlalu membantu. Lihat biola di sini untuk perilaku Oracle dalam keadaan ini, nama kuncinya akan bervariasi dari biola ke biola, tetapi beberapa string arbitrer dimulai dengan SYS _...)

Mempertimbangkan pernyataan Anda:

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

Ini akan menjadi "bagus untuk dimiliki" jika RDBMS dapat secara otomatis membuat bidang yang Anda inginkan dengan tipe data yang cocok dengan bidang yang dirujuk. Yang ingin saya katakan adalah bahwa mengubah DDL adalah (atau setidaknya seharusnya) operasi yang jarang digunakan dan bukan sesuatu yang ingin Anda lakukan secara teratur. Ini juga berisiko menambah dokumentasi yang sudah cukup besar.

Setidaknya PostgreSQL mencoba melakukan sesuatu yang masuk akal - itu menggabungkan nama tabel, nama FOREIGN KEYbidang _fkeydan bahkan menambahkan DETAIL: Key (sender_id)=(56) is not present in table "user_".untuk memberikan sesuatu yang mungkin masuk akal bagi manusia - lihat biola di sini .

Vérace
sumber
2
Saya tidak pernah menyebutkan nama kunci asing saya. Mereka diberi nama otomatis, dan biasanya cukup bermanfaat. Misalnya, nama default dalam konteks itu adalah "y_z_fkey". Aku berpendapat bahwa nama yang lebih baik dari y_x_fkeykarena melanggar Anda tidak memberitahu Anda kolom Anda Memasukkan sedang dalam yang menyebabkan kesalahan. Saya tidak terlalu peduli tentang ke mana arahnya. Sebagai aturan umum, Anda harus TIDAK PERNAH menamai fkeys Anda dan membiarkan PostgreSQL default menanganinya.
Evan Carroll
Selain itu, Anda mungkin juga tidak ingin mengganti default ON UPDATE CASCADE ON DELETE CASCADE;dalam contoh, terutama tanpa alasan. Itu membuat contoh lebih kompleks, dan Anda tidak perlu repot menjelaskan apa itu. Saya untuk satu biasanya tidak ingin menghapus kaskade.
Evan Carroll
1
Saya selalu memberi nama FK, sesuai dengan konvensi yang diputuskan perusahaan / proyek. Tidak masalah apakah itu y_x_fkeyatau y_z_fkeyatau x__y_FK, asalkan konsisten.
ypercubeᵀᴹ
Saya akan sangat setuju dengan ini jika Anda membuat kontrak - pilih satu konvensi dan patuhi itu dan / atau pastikan bahwa Anda sesuai dengan konvensi (s) yang dulu pernah digunakan dengan sistem.
Vérace
@ EvanCarroll - jika konvensi PostgreSQL adalah tentang proyek atau yang sebelumnya diputuskan pada sistem yang mungkin bukan PostgreSQL - suatu sistem mungkin telah dimulai, katakanlah, Oracle atau sistem lain yang mungkin tidak memiliki konvensi PostgreSQL. Anda bisa membantah bahwa x_y_z_fk mungkin memberikan informasi sebanyak mungkin jika terjadi kesalahan! Pilih sesuatu dan patuhi itu adalah moto saya, tetapi jangan biarkan satu RDBMS (tidak peduli seberapa baik) memutuskan konvensi untuk Anda!
Vérace
8

Saya tidak yakin mengapa semua orang mengatakan kepada Anda bahwa Anda harus melakukan ini dalam dua langkah. Faktanya, Anda tidak . Anda mencoba menambahkan FOREIGN KEYyang mengasumsikan, dengan desain, kolom ada di sana dan melemparkan kesalahan itu jika kolom tidak ada di sana. Jika Anda menambahkan COLUMN, Anda dapat membuatnya secara eksplisit FOREIGN KEYpada kreasi dengan REFERENCES,

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

Akan bekerja dengan baik. Anda dapat melihat sintaks di ALTER TABLEsini,

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

Dengan "action" sebagai,

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

Contoh-contoh ini bahkan ada dalam dokumen,

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;

Tetapi semua itu tidak diperlukan karena kita bisa mengandalkan autonaming dan resolusi kunci utama (jika hanya nama tabel yang ditentukan maka Anda merujuk kunci utama).

Evan Carroll
sumber
0

CASE1: Jika Anda perlu membuat kunci asing saat membuat tabel baru

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

Perintah di atas akan membuat tabel dengan nama 'table1' dan tiga kolom bernama 'id' (kunci utama), 'kolom1', 'table2_id' (kunci asing dari table1 yang mereferensikan kolom id dari table2).

'Seri' DATATYPE akan membuat kolom yang menggunakan tipe data ini sebagai kolom yang dibuat secara otomatis, saat memasukkan nilai ke dalam tabel Anda tidak perlu menyebutkan kolom ini sama sekali, atau Anda dapat memberikan 'default' tanpa tanda kutip di tempat nilai.

Kolom kunci utama selalu ditambahkan ke indeks tabel dengan nilai 'tablename_pkey'.

Jika kunci asing ditambahkan pada waktu pembuatan tabel, CONSTRAINT ditambahkan dengan pola '(present_table_name) _ (foreign_key_id_name) _fkey'.

Ketika menambahkan kunci asing, kita harus memasukkan kata kunci 'REFERENSI' di sebelah nama kolom karena kita ingin memberi tahu postgres bahwa kolom ini mereferensikan sebuah tabel dan kemudian di samping referensi kita harus memberikan tabel untuk referensi dan dalam kurung memberikan nama kolom dari tabel yang direferensikan, biasanya kunci asing diberikan sebagai kolom kunci utama.

KASUS 2: Jika Anda ingin kunci asing ke tabel yang ada di kolom yang ada

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

CATATAN: tanda kurung '()' setelah KUNCI LUAR NEGERI dan REFERENSI tabel2 adalah wajib atau postgres lain akan menimbulkan kesalahan.

Ashok Allu
sumber
0

Saya tahu masalahnya. Nama kolom berbeda. Mungkin dalam satu kolom, ada ruang tambahan setelah nama kolom Anda, jadi harap hati-hati memastikan nama kolom Anda sama persis.

XIN WANG
sumber
1
OP bertanya: Apakah pernyataan ini tidak membuat kolom juga? Jadi jelas bahwa dia berharap itu terjadi.
Laurenz Albe