ORACLE tidak mengizinkan nilai NULL di salah satu kolom yang terdiri dari kunci utama. Tampaknya hal yang sama berlaku untuk sebagian besar sistem "tingkat perusahaan" lainnya.
Pada saat yang sama, sebagian besar sistem juga memungkinkan hambatan unik pada kolom yang dapat dibatalkan.
Mengapa kendala unik dapat memiliki NULL tetapi kunci primer tidak bisa? Apakah ada alasan logis mendasar untuk ini, atau ini lebih merupakan batasan teknis?
database
database-design
Roman Starkov
sumber
sumber
Jawaban:
Kunci primer adalah untuk baris pengidentifikasi unik. Ini dilakukan dengan membandingkan semua bagian kunci dengan input.
Per definisi, NULL tidak dapat menjadi bagian dari perbandingan yang berhasil. Bahkan perbandingan dengan dirinya sendiri (
NULL = NULL
) akan gagal. Ini berarti kunci yang berisi NULL tidak akan berfungsi.Selain itu, NULL diizinkan dalam kunci asing, untuk menandai hubungan opsional. (*) Membiarkannya di PK juga akan merusak ini.
(*) Kata peringatan: Memiliki kunci asing nullable tidak bersih desain basis data relasional.
Jika ada dua entitas
A
dan diB
manaA
secara opsional dapat dikaitkanB
, solusi bersih adalah membuat tabel resolusi (katakanlahAB
). Tabel yang akan menghubungkanA
denganB
: Jika ada adalah hubungan maka akan berisi catatan, jika ada tidak maka tidak akan.sumber
Kunci utama mendefinisikan pengidentifikasi unik untuk setiap baris dalam sebuah tabel: ketika sebuah tabel memiliki kunci utama, Anda memiliki cara yang dijamin untuk memilih baris mana pun darinya.
Batasan unik tidak selalu mengidentifikasi setiap baris; itu hanya menentukan bahwa jika suatu baris memiliki nilai dalam kolomnya, maka mereka harus unik. Ini tidak cukup untuk mengidentifikasi secara unik setiap baris, yang harus dilakukan oleh kunci utama.
sumber
Secara mendasar tidak ada yang salah dengan NULL dalam kunci utama multi-kolom. Tetapi memiliki satu implikasi yang kemungkinan tidak diinginkan oleh perancang, itulah sebabnya banyak sistem membuat kesalahan ketika Anda mencoba ini.
Pertimbangkan kasus versi modul / paket yang disimpan sebagai serangkaian bidang:
5 elemen pertama dari kunci utama secara teratur didefinisikan sebagai bagian dari versi rilis, tetapi beberapa paket memiliki ekstensi khusus yang biasanya bukan bilangan bulat (seperti "rc-foo" atau "vanilla" atau "beta" atau apa pun yang dilakukan seseorang untuk yang empat bidang tidak mencukupi mungkin bermimpi). Jika sebuah paket tidak memiliki ekstensi, maka itu NULL dalam model di atas, dan tidak ada salahnya dilakukan dengan meninggalkan hal-hal seperti itu.
Tapi apa itu NULL? Seharusnya mewakili kurangnya informasi, tidak diketahui. Yang mengatakan, mungkin ini lebih masuk akal:
Dalam versi ini bagian "ext" dari tuple BUKAN NULL tetapi default ke string kosong - yang secara semantik (dan praktis) berbeda dari NULL. NULL adalah suatu yang tidak diketahui, sedangkan string kosong adalah catatan yang disengaja dari "sesuatu yang tidak ada". Dengan kata lain, "kosong" dan "nol" adalah hal yang berbeda. Perbedaan antara "Saya tidak punya nilai di sini" dan "Saya tidak tahu apa nilai di sini."
Ketika Anda mendaftarkan paket yang tidak memiliki ekstensi versi, Anda tahu itu tidak memiliki ekstensi, jadi string kosong sebenarnya adalah nilai yang benar. NULL hanya akan benar jika Anda tidak tahu apakah ekstensi itu atau tidak, atau Anda tahu itu ekstensi tetapi tidak tahu apa itu ekstensi. Situasi ini lebih mudah untuk ditangani dalam sistem di mana nilai string adalah norma, karena tidak ada cara untuk mewakili "integer kosong" selain memasukkan 0 atau 1, yang akhirnya akan digulung dalam perbandingan apa pun yang dibuat kemudian (yang memiliki implikasinya sendiri) *.
Kebetulan, kedua cara ini valid di Postgres (karena kita sedang membahas "perusahaan" RDMBSs), tetapi hasil perbandingan dapat sedikit berbeda ketika Anda melempar NULL ke dalam campuran - karena NULL == "tidak tahu" jadi semua hasil perbandingan yang melibatkan NULL akhirnya menjadi NULL karena Anda tidak dapat mengetahui sesuatu yang tidak dikenal. BAHAYA! Pikirkan baik-baik tentang itu: ini berarti bahwa hasil perbandingan NULL menyebar melalui serangkaian perbandingan. Ini bisa menjadi sumber bug halus saat menyortir, membandingkan, dll.
Postgres mengasumsikan Anda sudah dewasa dan dapat mengambil keputusan sendiri. Oracle dan DB2 menganggap Anda tidak menyadari bahwa Anda melakukan sesuatu yang konyol dan membuat kesalahan. Ini biasanya hal yang benar, tetapi tidak selalu - Anda mungkin sebenarnya tidak tahu dan memiliki NULL dalam beberapa kasus dan karenanya meninggalkan baris dengan elemen yang tidak diketahui yang tidak mungkin dilakukan perbandingan yang berarti adalah perilaku yang benar.
Dalam kasus apa pun Anda harus berusaha untuk menghilangkan jumlah bidang NULL yang Anda izinkan di seluruh skema dan dua kali lipat ketika menyangkut bidang yang merupakan bagian dari kunci utama. Dalam sebagian besar kasus, keberadaan kolom NULL merupakan indikasi rancangan skema yang tidak dinormalisasi (berlawanan dengan yang tidak dinormalisasi dengan sengaja) dan harus dipikirkan dengan keras sebelum diterima.
[* CATATAN: Dimungkinkan untuk membuat tipe khusus yang merupakan gabungan bilangan bulat dan tipe "bawah" yang secara semantik berarti "kosong" sebagai kebalikan dari "tidak dikenal". Sayangnya ini memperkenalkan sedikit kompleksitas dalam operasi perbandingan dan biasanya benar-benar mengetik benar tidak sepadan dengan usaha dalam praktik karena Anda tidak boleh diizinkan banyak
NULL
nilai sama sekali di tempat pertama. Yang mengatakan, akan luar biasa jika RDBMS akan memasukkanBOTTOM
tipe standar selainNULL
untuk mencegah kebiasaan santai semantik semantik "tidak ada nilai" dengan "nilai tidak diketahui". ]sumber
NULL == NULL -> false (setidaknya dalam DBMSs)
Jadi, Anda tidak akan dapat mengambil hubungan apa pun menggunakan nilai NULL bahkan dengan kolom tambahan dengan nilai nyata.
sumber
where pk_1 = 'a' and pk_2 = 'b'
dengan nilai normal, dan beralih kewhere pk_1 is null and pk_2 = 'b'
ketika ada nol.where (a.pk1 = b.pk1 or (a.pk1 is null and b.pk1 is null)) and (a.pk2 = b.pk2 or (a.pk2 is null and b.pk2 is null))
/Jawaban oleh Tony Andrews adalah jawaban yang layak. Tetapi jawaban sebenarnya adalah bahwa ini telah menjadi konvensi yang digunakan oleh komunitas basis data relasional dan BUKAN suatu keharusan. Mungkin itu adalah konvensi yang bagus, mungkin juga tidak.
Membandingkan apa pun dengan NULL menghasilkan TIDAK DIKETAHUI (nilai kebenaran ke-3). Jadi seperti yang telah disarankan dengan nol semua kearifan tradisional tentang kesetaraan keluar jendela. Yah begitulah tampaknya pada pandangan pertama.
Tapi saya tidak berpikir ini perlu begitu dan bahkan database SQL tidak berpikir bahwa NULL menghancurkan semua kemungkinan untuk perbandingan.
Jalankan di basis data Anda kueri SELECT * FROM VALUES (NULL) UNION SELECT * FROM VALUES (NULL)
Yang Anda lihat hanyalah satu tuple dengan satu atribut yang memiliki nilai NULL. Jadi, serikat pekerja mengakui di sini dua nilai NULL sebagai sama.
Ketika membandingkan kunci komposit yang memiliki 3 komponen untuk tupel dengan 3 atribut (1, 3, NULL) = (1, 3, NULL) <=> 1 = 1 DAN 3 = 3 DAN NULL = NULL Hasil dari ini TIDAK DIKETAHUI .
Tapi kita bisa mendefinisikan operator perbandingan jenis baru misalnya. ==. X == Y <=> X = Y ATAU (X IS NULL DAN Y IS NULL)
Memiliki operator kesetaraan semacam ini akan membuat kunci komposit dengan komponen nol atau kunci non-komposit dengan nilai nol tidak bermasalah.
sumber
Saya masih percaya ini adalah kelemahan mendasar / fungsional yang disebabkan oleh masalah teknis. Jika Anda memiliki bidang opsional tempat Anda dapat mengidentifikasi pelanggan, kini Anda harus meretas nilai tiruan ke dalamnya, hanya karena NULL! = NULL, tidak terlalu elegan namun ini merupakan "standar industri"
sumber