Desain basis data: Normalisasi hubungan "(banyak-ke-banyak)-ke-banyak"

13

Versi pendek

Saya harus menambahkan sejumlah properti tambahan ke setiap pasangan dalam banyak-ke-banyak bergabung. Melompati diagram di bawah ini, manakah dari Pilihan 1-4 yang merupakan cara terbaik, dalam hal kelebihan dan kekurangan, untuk mencapai ini dengan memperluas Base Case? Atau, adakah alternatif yang lebih baik yang belum saya pertimbangkan di sini?

Versi yang lebih panjang

Saat ini saya memiliki dua tabel dalam hubungan banyak-ke-banyak, melalui tabel gabungan antara. Sekarang saya perlu menambahkan tautan tambahan ke properti yang dimiliki oleh sepasang objek yang ada. Saya memiliki nomor tetap dari properti ini untuk setiap pasangan, meskipun satu entri dalam tabel properti mungkin berlaku untuk beberapa pasangan (atau bahkan dapat digunakan beberapa kali untuk satu pasangan). Saya mencoba menentukan cara terbaik untuk melakukan ini, dan saya mengalami kesulitan memilah bagaimana memikirkan situasi. Semantik sepertinya saya bisa menggambarkannya sebagai salah satu dari yang sama baiknya:

  1. Satu pasang tertaut ke satu set sejumlah properti tetap
  2. Satu pasang tertaut ke banyak properti tambahan
  3. Banyak (dua) objek yang ditautkan ke satu set properti
  4. Banyak objek yang terhubung ke banyak properti

Contoh

Saya memiliki dua jenis objek, X dan Y, masing-masing dengan ID unik, dan tabel penautan objx_objydengan kolom x_iddan y_id, yang bersama-sama membentuk kunci utama untuk tautan tersebut. Setiap X dapat dikaitkan dengan banyak Ys, dan sebaliknya. Ini adalah pengaturan untuk hubungan many-to-many saya yang sudah ada.

Kasus Dasar

Kasing dasar

Sekarang saya juga memiliki satu set properti yang didefinisikan dalam tabel lain, dan satu set kondisi di mana pasangan (X, Y) yang diberikan harus memiliki properti P. Jumlah kondisi ditetapkan, dan sama untuk semua pasangan. Mereka pada dasarnya mengatakan "Dalam situasi C1, pasangan (X1, Y1) memiliki properti P1", "Dalam situasi C2, pasangan (X1, Y1) memiliki properti P2", dan seterusnya, untuk tiga situasi / kondisi untuk setiap pasangan dalam gabungan meja.

Pilihan 1

Dalam situasi saya saat ini ada tepat tiga kondisi seperti itu, dan saya tidak memiliki alasan untuk berharap bahwa untuk meningkat, sehingga satu kemungkinan adalah dengan menambahkan kolom c1_p_id, c2_p_iddan c3_p_iduntuk featx_featy, menentukan untuk diberikan x_iddan y_id, yang properti p_iduntuk penggunaan di masing-masing tiga kasus .

Pilihan 1

Ini sepertinya bukan ide yang bagus bagi saya, karena mempersulit SQL untuk memilih semua properti yang diterapkan pada fitur, dan tidak mudah mengubah ke kondisi yang lebih banyak. Namun, itu menegakkan persyaratan sejumlah kondisi tertentu per (X, Y) pasangan. Faktanya, ini adalah satu-satunya pilihan di sini yang melakukannya.

pilihan 2

Buat tabel kondisi cond, dan tambahkan ID kondisi ke kunci utama tabel bergabung.

pilihan 2

Satu kelemahan untuk ini adalah tidak menentukan jumlah kondisi untuk setiap pasangan. Lain adalah ketika saya hanya mempertimbangkan hubungan awal, dengan sesuatu seperti

SELECT objx.*, objy.* FROM objx
  INNER JOIN objx_objy ON objx_objy.x_id = objx.id
  INNER JOIN objy ON objy.id = objx_objy.y_id

Saya kemudian harus menambahkan DISTINCTklausa untuk menghindari entri duplikat. Ini tampaknya telah kehilangan fakta bahwa setiap pasangan harus ada hanya sekali.

Opsi 3

Buat 'pasangan ID' baru di tabel bergabung, dan kemudian memiliki tabel tautan kedua antara yang pertama dan properti dan ketentuan.

Opsi 3

Ini tampaknya memiliki kelemahan paling sedikit, selain dari kurangnya menegakkan sejumlah kondisi tetap untuk setiap pasangan. Apakah masuk akal untuk membuat ID baru yang mengidentifikasi tidak lain dari ID yang ada?

Opsi 4 (3b)

Pada dasarnya sama dengan Opsi 3, tetapi tanpa pembuatan bidang ID tambahan. Ini dilakukan dengan menempatkan kedua ID asli di tabel bergabung baru, sehingga berisi x_iddan y_idbidang, bukan xy_id.

Opsi 4

Keuntungan tambahan untuk formulir ini adalah tidak mengubah tabel yang ada (meskipun belum diproduksi). Namun, pada dasarnya duplikat seluruh tabel beberapa kali (atau terasa seperti itu), jadi juga tidak ideal.

Ringkasan

Perasaan saya adalah bahwa Pilihan 3 dan 4 cukup mirip sehingga saya bisa memilih salah satunya. Saya mungkin akan memiliki sekarang jika bukan untuk persyaratan sejumlah kecil, tetap tautan ke properti, yang membuat Opsi 1 tampak lebih masuk akal daripada yang seharusnya. Berdasarkan beberapa pengujian yang sangat terbatas, menambahkan DISTINCTklausa ke pertanyaan saya tampaknya tidak mempengaruhi kinerja dalam situasi ini, tapi saya tidak yakin bahwa Opsi 2 mewakili situasi dan juga yang lain, karena duplikasi yang melekat yang disebabkan oleh penempatan pasangan (X, Y) yang sama berpasangan dalam beberapa baris pada tabel tautan.

Apakah salah satu dari opsi ini adalah cara terbaik saya ke depan, atau apakah ada struktur lain yang harus saya pertimbangkan?

Michael Underwood
sumber
Secara keseluruhan 1 dan 4 sepertinya pilihan terbaik, saya setuju. Tidak akan mudah untuk menegakkan jumlah properti tetap (3) dengan opsi 4 tapi saya pikir itu layak.
ypercubeᵀᴹ
Untuk DISTINCTklausa, saya memikirkan pertanyaan seperti yang ada di akhir # 2, yang menghubungkan xdan ymelaluinya xyctetapi tidak merujuk ke c... Jadi jika saya (x_id, y_id, c_id)dibatasi UNIQUEdengan baris (1,1,1)dan (1,1,2), kemudian SELECT x.id, y.id FROM x JOIN xyc JOIN y, saya akan mendapatkan kembali dua yang identik baris (1,1),, dan (1,1).
Michael Underwood
1
Ah baiklah. Saya akan mengabaikan opsi 2 saja. Saya akan memilih 1 atau 4.
ypercubeᵀᴹ
Semakin saya memikirkannya, semakin saya merasa bahwa membatasi jumlah properti menjadi tiga adalah yang paling tidak penting dari persyaratan saya. Jadi, kecuali umpan balik konstruktif tambahan dalam beberapa saat ke depan, saya mungkin akan pergi dengan # 4 pada saat ini. Terima kasih atas masukan Anda, @ypercube!
Michael Underwood

Jawaban:

7
  • Pilihan 1

    * Ini sepertinya bukan ide bagus bagi saya, karena mempersulit SQL untuk memilih semua properti yang diterapkan pada fitur ...

    Itu tidak selalu mempersulit query SQL (lihat kesimpulan di bawah).

    ... dan tidak mudah menyesuaikan dengan kondisi yang lebih banyak ...

    Ini berskala mudah ke lebih banyak kondisi, selama masih ada sejumlah kondisi, dan tidak ada lusinan atau ratusan.

    Namun, itu menegakkan persyaratan sejumlah kondisi tertentu per (X, Y) pasangan. Faktanya, ini adalah satu-satunya pilihan di sini yang melakukannya. *

    Ya, dan meskipun Anda mengatakan dalam komentar bahwa ini "yang paling penting dari persyaratan saya", Anda belum mengatakan itu tidak masalah sama sekali.

  • pilihan 2

    Satu kelemahan untuk ini adalah tidak menentukan jumlah kondisi untuk setiap pasangan. Lain adalah bahwa ketika saya hanya mempertimbangkan hubungan awal ... Saya kemudian harus menambahkan klausa DISTINCT untuk menghindari entri duplikat ...

    Saya pikir Anda dapat mengabaikan opsi ini karena komplikasi yang Anda sebutkan. The objx_objytabel mungkin meja mengemudi untuk beberapa pertanyaan Anda (misalnya "pilih semua properti diterapkan untuk fitur", yang saya mengambil berarti semua properti diterapkan pada objxatau objy). Anda dapat menggunakan tampilan untuk melakukan pra-penerapan DISTINCTsehingga bukan masalah mempersulit kueri, tapi itu akan mengukur skala kinerja yang sangat buruk untuk keuntungan yang sangat sedikit.

  • Opsi 3

    Apakah masuk akal untuk membuat ID baru yang mengidentifikasi tidak lain dari ID yang ada?

    Tidak, tidak - Opsi 4 lebih baik dalam segala hal.

  • Opsi 4

    ... pada dasarnya menduplikasi seluruh tabel beberapa kali (atau terasa seperti itu, toh) jadi juga sepertinya tidak ideal.

    Opsi ini baik-baik saja - ini adalah cara yang jelas mengatur hubungan jika jumlah properti variabel atau dapat berubah

Kesimpulan

Preferensi saya akan menjadi opsi 1 jika jumlah properti per objx_objycenderung stabil, dan jika Anda tidak dapat membayangkan pernah menambahkan lebih dari beberapa tambahan. Ini juga satu-satunya opsi yang memberlakukan batasan 'jumlah properti = 3' - menegakkan batasan serupa pada opsi 4 kemungkinan akan melibatkan penambahan c1_p_id... kolom ke tabel xy pula *.

Jika Anda benar-benar tidak terlalu peduli dengan kondisi itu, dan Anda juga memiliki alasan untuk meragukan bahwa jumlah kondisi properti akan stabil, maka pilih opsi 4.

Jika Anda tidak yakin yang mana, pilih opsi 1 - itu lebih sederhana dan itu pasti lebih baik jika Anda memiliki pilihan, seperti yang dikatakan orang lain. Jika Anda menunda opsi 1 "... karena mempersulit SQL untuk memilih semua properti yang diterapkan ke fitur ..." Saya sarankan membuat tampilan untuk memberikan data yang sama dengan tabel tambahan dalam opsi 4:

tabel opsi 1:

create table prop(id integer primary key);
create table objx(id integer primary key);
create table objy(id integer primary key);

create table objx_objy(
  x_id integer references objx
, y_id integer references objy
, c1_p_id integer not null references prop
, c2_p_id integer not null references prop
, c3_p_id integer not null references prop
, primary key (x_id, y_id)
);

insert into prop(id) select generate_series(90,99);
insert into objx(id) select generate_series(10,12);
insert into objy(id) select generate_series(20,22);

insert into objx_objy(x_id,y_id,c1_p_id,c2_p_id,c3_p_id)
select objx.id, objy.id, 90, 91, 90+floor(random()*10)
from objx cross join objy;

lihat untuk 'meniru' opsi 4:

create view objx_objy_prop as
select x_id
     , y_id
     , unnest(array[1,2,3]) c_id
     , unnest(array[c1_p_id,c2_p_id,c3_p_id]) p_id
from objx_objy;

"pilih semua properti yang diterapkan ke fitur":

select distinct p_id from objx_objy_prop where x_id=10 order by p_id;

/*
|p_id|
|---:|
|  90|
|  91|
|  97|
|  98|
*/

Aku di sini

Jack berkata coba topanswers.xyz
sumber
-3

Saya percaya salah satu dari opsi ini dapat bekerja, tetapi saya akan menggunakan opsi 1 jika jumlah kondisi benar-benar tetap pada 3, dan opsi 2 jika tidak. Pisau cukur Occam juga berfungsi untuk desain basis data, semua faktor lain yang setara dengan desain yang paling sederhana biasanya adalah yang terbaik.

Meskipun jika Anda ingin mengikuti aturan normalisasi basis data yang ketat, saya yakin Anda harus menggunakan 2 terlepas dari apakah jumlah kondisi sudah diperbaiki.

Matthew Sontum
sumber