Bisakah tabel tabel dengan Kunci Asing menjadi NULL?

235

Saya memiliki tabel yang memiliki beberapa kolom ID ke tabel lain.

Saya ingin kunci asing memaksa integritas hanya jika saya memasukkan data ke sana. Jika saya melakukan pembaruan di lain waktu untuk mengisi kolom itu, maka itu juga harus memeriksa kendala.

(Ini kemungkinan tergantung pada database server, saya menggunakan tipe tabel MySQL & InnoDB)

Saya percaya ini adalah harapan yang masuk akal, tetapi perbaiki jika saya salah.

bender
sumber
6
Saya tidak tahu tentang MySQL, tetapi MS SQL Server memungkinkan kunci asing dapat dibatalkan dengan semantik yang Anda inginkan. Saya berharap itu adalah perilaku standar.
Jeffrey L Whitledge
1
kunci asing, tidak bisa nol secara default di mySQL, alasannya sederhana, jika Anda mereferensikan sesuatu dan Anda membiarkannya nol, Anda akan kehilangan integritas data. ketika Anda membuat set tabel, biarkan null untuk TIDAK dan kemudian menerapkan batasan kunci asing. Anda tidak dapat mengatur null pada pembaruan, itu akan mengirimkan Anda kesalahan, tetapi Anda dapat (Anda harus) tidak memperbarui kolom ini dan hanya memperbarui bidang yang perlu Anda ubah.
JoelBonetR

Jawaban:

245

Ya, Anda bisa memberlakukan batasan hanya ketika nilainya tidak NULL. Ini dapat dengan mudah diuji dengan contoh berikut:

CREATE DATABASE t;
USE t;

CREATE TABLE parent (id INT NOT NULL,
                     PRIMARY KEY (id)
) ENGINE=INNODB;

CREATE TABLE child (id INT NULL, 
                    parent_id INT NULL,
                    FOREIGN KEY (parent_id) REFERENCES parent(id)
) ENGINE=INNODB;


INSERT INTO child (id, parent_id) VALUES (1, NULL);
-- Query OK, 1 row affected (0.01 sec)


INSERT INTO child (id, parent_id) VALUES (2, 1);

-- ERROR 1452 (23000): Cannot add or update a child row: a foreign key 
-- constraint fails (`t/child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY
-- (`parent_id`) REFERENCES `parent` (`id`))

Sisipan pertama akan lulus karena kami menyisipkan NULL di parent_id. Sisipan kedua gagal karena batasan kunci asing, karena kami mencoba memasukkan nilai yang tidak ada dalam parenttabel.

Daniel Vassallo
sumber
16
Tabel induk juga dapat dideklarasikan dengan id INT NOT NULL.
Will
@ CJDennis Jika Anda membuatnya sehingga hanya satu baris dapat memiliki ID nol, itu bisa digunakan sebagai nilai cadangan untuk baris lain. (Meskipun itu mungkin bekerja lebih baik untuk DB jika Anda hanya menggunakan lebih banyak kolom.) Kendala default sepertinya masalah jika Anda ingin tahu nanti apakah nilai awalnya ditetapkan sebagai "default" (dengan menggunakan nol) atau setel ke nilai yang kebetulan sama dengan "default". Dengan memiliki baris dengan id nol, Anda dapat dengan jelas menunjukkan bahwa baris ini tidak boleh digunakan sebagai baris normal, dan dapat menggunakan baris sebagai cara memberikan semacam nilai default dinamis untuk baris lain.
Ouroborus
1
Saya pikir parent_id INT NULLbagian ini (secara verbal) sama denganparent_id int default null
Catatan tambahan untuk pengguna java, jika Anda menggunakan ibatis atau ORM lainnya dan menggunakan primitif intalih-alih Integeranggota kelas Anda, defaultnya tidak akan pernah menjadi nol, tetapi akan menjadi 0 dan Anda akan gagal batasannya.
Jim Ford
32

Saya menemukan bahwa ketika menyisipkan, nilai kolom nol harus secara khusus dinyatakan sebagai NULL, jika tidak saya akan mendapatkan kesalahan pelanggaran kendala (sebagai lawan dari string kosong).

Backslider
sumber
8
Bisakah Anda tidak menetapkan nilai default NULL pada kolom untuk memperbolehkan ini?
Kevin Coulombe
Ya, dalam sebagian besar bahasa, NULL berbeda dari string kosong. Mungkin halus saat mulai, tetapi penting untuk diingat.
Gary
Hai Backslider, Anda mengatakan "(sebagai lawan dari string kosong)", tapi saya tidak berpikir Anda bermaksud bahwa Anda akan MENYAMPAIKAN nilai string kosong, melainkan, bahwa Anda tidak menentukan nilai pada untuk Nilai pada semua? yaitu Anda bahkan tidak menyebutkan kolom di Anda INSERT INTO {table} {list_of_columns}? Karena itu berlaku untuk saya; menghilangkan penyebutan kolom menyebabkan kesalahan, tetapi termasuk dan secara eksplisit mengatur untuk memperbaiki kesalahan NULL. Jika saya benar, saya pikir komentar @ Gary tidak berlaku (karena Anda tidak bermaksud string kosong), tetapi @Kevin Coulombe bisa membantu ...
The Red Pea
Ya, saran KevinCoulombe berhasil, saya menjelaskan cara mencapainya dengan skrip Migrasi Entity Framework Core, di sini
The Red Pea
Penting untuk menunjukkan bahwa alasan untuk menjadi eksplisit ketika memperbarui catatan yang berisi kunci asing NULL hanya berlaku untuk tipe string (varchar, dll), karena kalau tidak, string kosong dapat diteruskan sebagai default. Ini adalah kasus dengan MySQL, dan menghasilkan kesalahan integritas saat pembaruan.
CodeMantle
4

Ya, itu akan berfungsi seperti yang Anda harapkan. Sayangnya, saya sepertinya kesulitan menemukan pernyataan eksplisit tentang hal ini di manual MySQL .

Kunci asing berarti nilainya harus ada di tabel lain. NULL mengacu pada tidak adanya nilai, jadi ketika Anda mengatur kolom ke NULL, tidak masuk akal untuk mencoba menegakkan batasan itu.

davidtbernal
sumber
Dengan desain Foreign Key harus merujuk ke beberapa kunci (Primer) yang bukan NULL, tetapi selama fase pengembangan ketika kita perlu memiliki beberapa data pertama kali dimasukkan ke tabel anak, yang kita tidak tahu kepada siapa itu akan merujuk (tabel induk) . Itu sebabnya kami memiliki nilai NULL yang diizinkan. Dalam produksi memiliki NULL akan menjadi aliran desain, yang dapat dikatakan kasar.
vimal krishna
2

Di atas berfungsi tetapi ini tidak. Catat ON DELETE CASCADE

CREATE DATABASE t;
USE t;

CREATE TABLE parent (id INT NOT NULL,
                 PRIMARY KEY (id)
) ENGINE=INNODB;

CREATE TABLE child (id INT NULL, 
                parent_id INT NULL,
                FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE

) ENGINE=INNODB;


INSERT INTO child (id, parent_id) VALUES (1, NULL);
-- Query OK, 1 row affected (0.01 sec)
MrFabulous
sumber
4
Apa yang Anda maksud dengan 'yang di atas'? Perhatikan bahwa jika Anda merujuk ke jawaban lain, urutannya dapat berubah.
d219
2

Ya, nilainya bisa NULL, tetapi Anda harus eksplisit. Saya telah mengalami situasi yang sama ini sebelumnya, dan mudah untuk melupakan MENGAPA ini terjadi, sehingga perlu sedikit untuk mengingat apa yang perlu dilakukan.

Jika data yang dikirim dicetak atau ditafsirkan sebagai string kosong, itu akan gagal. Namun, dengan secara eksplisit menetapkan nilai ke NULL saat INSERTING atau UPDATING, Anda siap melakukannya.

Tapi ini menyenangkan pemrograman, bukan? Buat masalah kita sendiri dan perbaiki! Bersulang!

Toby Crain
sumber
1

Cara lain untuk ini adalah dengan memasukkan elemen DEFAULT di tabel lain. Misalnya, referensi apa pun ke uuid = 00000000-0000-0000-0000-000000000000 pada tabel lainnya akan menunjukkan tidak ada tindakan. Anda juga perlu mengatur semua nilai untuk id itu menjadi "netral", misalnya 0, string kosong, nol agar tidak mempengaruhi logika kode Anda.

Wildhammer
sumber
2
Ini bukan hal yang sama. Nilai default atau "netral" tidak sama dengan NULL, tidak adanya nilai. Tanpa mendiskusikan kelebihan nilai default di atas NULL, frase Anda dicampur sedikit. "Cara lain untuk menyisipkan elemen null di tabel lain" harus mengatakan sesuatu yang lebih seperti "Cara lain untuk menyisipkan elemen DEFAULT di tabel lain"
blindguy
0

Saya juga terjebak pada masalah ini. Tapi saya memecahkan hanya dengan mendefinisikan kunci asing sebagai unsigned integer. Temukan contoh di bawah ini-

CREATE TABLE parent (
   id int(10) UNSIGNED NOT NULL,
    PRIMARY KEY (id)
) ENGINE=INNODB;

CREATE TABLE child (
    id int(10) UNSIGNED NOT NULL,
    parent_id int(10) UNSIGNED DEFAULT NULL,
    FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE
) ENGINE=INNODB;
Syams Reza
sumber