Saya punya tabel containers
yang dapat memiliki hubungan banyak-ke-banyak ke beberapa tabel, katakanlah itu plants
, animals
dan bacteria
. Setiap wadah dapat berisi tanaman, hewan, atau bakteri dalam jumlah yang sewenang-wenang, dan setiap tanaman, hewan, atau bakteri dapat dalam jumlah yang sewenang-wenang.
Sejauh ini ini sangat mudah, tetapi bagian saya memiliki masalah adalah bahwa setiap wadah hanya boleh mengandung unsur-unsur dari jenis yang sama. Wadah campuran yang mis. Berisi tumbuhan dan hewan harus menjadi pelanggaran batasan dalam basis data.
Skema asli saya untuk ini adalah sebagai berikut:
containers
----------
id
...
...
containers_plants
-----------------
container_id
plant_id
containers_animals
------------------
container_id
animal_id
containers_bacteria
-------------------
container_id
bacterium_id
Tetapi dengan skema ini, saya tidak dapat menemukan cara mengimplementasikan batasan bahwa kontainer harus homogen.
Apakah ada cara untuk menerapkan ini dengan integritas referensial dan memastikan pada tingkat basis data bahwa wadahnya homogen?
Saya menggunakan Postgres 9.6 untuk ini.
sumber
Jawaban:
Ada cara untuk mengimplementasikan ini secara deklaratif hanya tanpa mengubah pengaturan Anda saat ini, jika Anda setuju untuk memperkenalkan beberapa redundansi padanya. Berikut ini dapat dianggap sebagai pengembangan atas saran RDFozz , meskipun ide tersebut sepenuhnya terbentuk dalam pikiran saya sebelum saya membaca jawabannya (dan itu cukup berbeda untuk menjamin posting jawabannya sendiri).
Penerapan
Inilah yang Anda lakukan, langkah demi langkah:
Buat
containerTypes
tabel di sepanjang garis yang disarankan dalam jawaban RDFozz:Isi itu dengan ID yang telah ditentukan untuk setiap jenis. Untuk tujuan jawaban ini, biarkan mereka cocok dengan contoh RDFozz: 1 untuk tanaman, 2 untuk hewan, 3 untuk bakteri.
Tambahkan
containerType_id
kolom kecontainers
dan buat itu tidak bisa dibatalkan dan kunci asing.Dengan asumsi
id
kolom sudah menjadi kunci utamacontainers
, buat batasan unik(id, containerType_id)
.Di sinilah redudansi dimulai. Jika
id
dinyatakan sebagai kunci utama, kami yakin itu unik. Jika unik, kombinasi dariid
dan kolom lain pasti unik juga tanpa deklarasi keunikan tambahan - jadi, apa gunanya? Intinya adalah bahwa dengan secara resmi mendeklarasikan pasangan kolom yang unik, kita membiarkannya dapat dirujuk , yaitu menjadi target dari batasan kunci asing, yang merupakan bagian dari bagian ini.Tambahkan
containerType_id
kolom untuk masing-masing tabel junction (containers_animals
,containers_plants
,containers_bacteria
). Menjadikannya kunci asing sepenuhnya opsional. Yang penting adalah memastikan kolom memiliki nilai yang sama untuk semua baris, berbeda untuk setiap tabel: 1 untukcontainers_plants
, 2 untukcontainers_animals
, 3 untukcontainers_bacteria
, sesuai dengan deskripsi dicontainerTypes
. Dalam setiap kasus, Anda juga dapat menjadikan nilai itu sebagai default untuk menyederhanakan pernyataan penyisipan Anda:Di setiap tabel persimpangan, buat pasangan kolom
(container_id, containerType_id)
referensi batasan kunci asingcontainers
.Jika
container_id
sudah didefinisikan sebagai referensicontainers
, jangan ragu untuk menghapus batasan itu dari setiap tabel karena tidak diperlukan lagi.Bagaimana itu bekerja
Dengan menambahkan kolom jenis kontainer dan membuatnya berpartisipasi dalam batasan kunci asing, Anda menyiapkan mekanisme yang mencegah perubahan jenis kontainer. Mengubah jenis dalam
containers
tipe hanya dimungkinkan jika kunci asing didefinisikan denganDEFERRABLE
klausa, yang seharusnya tidak ada dalam implementasi ini.Bahkan jika mereka ditangguhkan, mengubah tipe akan tetap tidak mungkin karena kendala cek di sisi lain
containers
hubungan tabel-persimpangan. Setiap tabel persimpangan hanya memungkinkan satu jenis wadah tertentu. Itu tidak hanya mencegah referensi yang ada mengubah tipe tetapi juga mencegah penambahan referensi tipe yang salah. Artinya, jika Anda memiliki wadah tipe 2 (hewan), Anda hanya dapat menambahkan item ke dalamnya menggunakan tabel di mana tipe 2 diizinkan, yang manacontainers_animals
, dan tidak akan dapat menambahkan baris yang merujuknya, misalnyacontainers_bacteria
, yang menerima hanya ketik 3 kontainer.Akhirnya, keputusan Anda sendiri untuk memiliki tabel yang berbeda untuk
plants
,,animals
danbacteria
, dan tabel persimpangan yang berbeda untuk setiap jenis entitas, sudah membuat wadah tidak mungkin memiliki item lebih dari satu jenis.Jadi, semua faktor ini digabungkan memastikan, dengan cara deklaratif murni, bahwa semua wadah Anda akan homogen.
sumber
Salah satu opsi adalah menambahkan a
containertype_id
keContainer
tabel. Jadikan kolom BUKAN NULL, dan kunci asing keContainerType
tabel, yang akan memiliki entri untuk setiap jenis item yang bisa dimasukkan ke dalam sebuah wadah:Untuk memastikan jenis wadah tidak dapat diubah, buat pemicu pembaruan yang memeriksa apakah
containertype_id
diperbarui, dan memutar kembali perubahan dalam kasus itu.Kemudian, dalam menyisipkan dan memperbarui pemicu pada tabel tautan kontainer Anda, periksa containertype_id terhadap jenis entitas dalam tabel itu, untuk memastikan mereka cocok.
Jika apa pun yang Anda masukkan ke dalam wadah harus sesuai dengan jenisnya, dan jenisnya tidak dapat diubah, maka semua yang ada di dalam wadah itu akan menjadi jenis yang sama.
CATATAN: Karena pemicu pada tabel tautan adalah apa yang akan memutuskan apa yang cocok, jika Anda perlu memiliki jenis wadah yang dapat memiliki tanaman dan hewan di dalamnya, Anda dapat membuat jenis itu, menetapkannya ke wadah, dan memeriksa untuk itu. . Jadi, Anda mempertahankan fleksibilitas jika beberapa hal berubah pada beberapa titik (misalnya, Anda mendapatkan jenis "majalah" dan "buku" ...).
CATATAN yang kedua: Jika sebagian besar yang terjadi pada wadah adalah sama, terlepas dari apa yang ada di dalamnya, maka ini masuk akal. Jika Anda memiliki hal-hal yang sangat berbeda yang terjadi (dalam sistem, bukan dalam realitas fisik kami) berdasarkan pada isi wadah, maka gagasan Evan Carroll untuk memiliki tabel terpisah untuk jenis wadah terpisah masuk akal dengan sangat baik. Solusi ini menetapkan bahwa wadah memiliki jenis yang berbeda pada saat pembuatan, tetapi menyimpannya dalam tabel yang sama. Jika Anda harus memeriksa jenis setiap kali Anda mengambil tindakan pada sebuah wadah, dan jika tindakan yang Anda lakukan tergantung pada jenisnya, tabel terpisah sebenarnya mungkin lebih cepat dan lebih mudah.
sumber
Jika Anda hanya membutuhkan 2 atau 3 kategori (tanaman / metazoa / bakteri) dan Anda ingin membuat model hubungan XOR, mungkin "busur" adalah solusi untuk Anda. Keuntungan: tidak perlu pemicu. Diagram contoh dapat ditemukan [di sini] [1]. Dalam situasi Anda, tabel "wadah" akan memiliki 3 kolom dengan batasan PERIKSA, yang memungkinkan tanaman atau hewan atau bakteri.
Ini mungkin tidak tepat jika akan ada kebutuhan untuk membedakan banyak kategori (misalnya genera, spesies, subspesies) di masa depan. Namun, untuk 2-3 grup / kategori ini dapat melakukan trik.
UPDATE: Terinspirasi oleh saran dan komentar kontributor, solusi berbeda yang memungkinkan banyak taksa (kelompok organisme terkait, diklasifikasikan oleh ahli biologi), dan menghindari nama tabel "spesifik" (PostgreSQL 9.5).
Kode DDL:
Data uji:
Pengujian:
Terima kasih kepada @RDFozz dan @Evan Carroll dan @ypercube untuk masukan dan kesabaran mereka (membaca / mengoreksi jawaban saya).
sumber
Pertama, saya setuju dengan @RDFozz pada pembacaan pertanyaan .. Namun dia mengangkat beberapa kekhawatiran pada jawaban Stefan ,
Untuk mengatasi kekhawatirannya, adil
PRIMARY KEY
UNIQUE
kendala untuk melindungi terhadap entri duplikat.EXCLUSION
kendala untuk memastikan wadahnya "homogen"c_id
untuk memastikan kinerja yang layak.Ini seperti apa,
Sekarang Anda dapat memiliki satu wadah dengan banyak hal, tetapi hanya satu jenis benda dalam satu wadah.
Dan itu semua diimplementasikan pada indeks GIST.
Piramida Agung Giza tidak memiliki apa pun di PostgreSQL.
sumber
Itu ide yang buruk.
Dan sekarang Anda tahu mengapa. =)
Saya percaya Anda terjebak pada gagasan pewarisan dari pemrograman berorientasi objek (OO). OO Inheritance memecahkan masalah dengan penggunaan kembali kode. Dalam SQL, kode redundan adalah yang paling sedikit dari masalah kita. Integritas adalah yang pertama dan terpenting. Kinerja seringkali kedua. Kami akan menikmati rasa sakit untuk dua yang pertama. Kami tidak memiliki "waktu kompilasi" yang dapat menghilangkan biaya.
Jadi lupakan saja obsesi Anda untuk menggunakan kembali kode. Wadah untuk tanaman, hewan, dan bakteri pada dasarnya berbeda di setiap tempat di dunia nyata. Komponen penggunaan kembali kode "memegang barang" tidak akan melakukannya untuk Anda. Pisahkan mereka. Tidak hanya akan membuat Anda lebih integritas dan lebih banyak kinerja, tetapi di masa depan Anda akan menemukan lebih mudah untuk memperluas skema Anda: setelah semua, dalam skema Anda, Anda sudah harus memecah item yang terkandung (tanaman, hewan, dll) , tampaknya paling tidak mungkin Anda harus memecah wadah. Anda tidak akan ingin mendesain ulang seluruh skema Anda saat itu.
sumber
plant_containers
, dan seterusnya. Hal-hal yang hanya membutuhkan wadah tanaman pilih hanya dariplant_containers
tabel. Hal-hal yang memerlukan wadah apa pun (yaitu mencari semua jenis wadah) dapat dilakukanUNION ALL
pada ketiga tabel dengan wadah.