Pola Desain - Satu dari Banyak Tabel Induk

13

Saya menemukan situasi dalam database cukup sering di mana tabel yang diberikan dapat FK ke salah satu dari sejumlah tabel induk. Saya telah melihat dua solusi untuk masalah ini, tetapi tidak ada yang secara pribadi memuaskan. Saya ingin tahu pola apa yang telah Anda lihat di sana? Apakah ada cara yang lebih baik untuk melakukannya?

Sebuah Contoh Yang Diciptakan
Misalkan sistem saya memiliki Alerts. Lansiran dapat diterima untuk berbagai objek - Pelanggan, Berita, dan Produk. Lansiran yang diberikan bisa untuk satu-dan-hanya satu item. Untuk alasan apa pun Pelanggan, Artikel, dan Produk bergerak cepat (atau dilokalkan) sehingga teks / data yang diperlukan tidak dapat ditarik ke dalam Lansiran saat membuat Lansiran. Dengan pengaturan ini, saya telah melihat dua solusi.

Catatan: DDL di bawah ini untuk SQL Server tetapi pertanyaan saya harus berlaku untuk DBMS.

Solusi 1 - Beberapa FKeys Dapat Diabaikan

Dalam solusi ini tabel yang menautkan ke satu dari banyak tabel memiliki beberapa Kolom FK (untuk singkatnya, DDL di bawah ini tidak menunjukkan pembuatan FK). BAIK - Dalam solusi ini, menyenangkan bahwa saya memiliki kunci asing. Null-optinality dari FK membuat ini nyaman dan relatif mudah untuk menambahkan data yang akurat. THE BAD Querying tidak bagus karena membutuhkan pernyataan N LEFT JOINS atau N UNION untuk mendapatkan data terkait. Dalam SQL Server, secara khusus LEFT JOIN mencegah pembuatan tampilan yang diindeks.

CREATE TABLE Product (
    ProductID    int identity(1,1) not null,
    CreateUTC    datetime2(7) not null,
     Name        varchar(100) not null
    CONSTRAINT   PK_Product Primary Key CLUSTERED (ProductID)
)
CREATE TABLE Customer (
    CustomerID  int identity(1,1) not null,
    CreateUTC   datetime2(7) not null,
     Name       varchar(100) not null
    CONSTRAINT  PK_Customer Primary Key CLUSTERED (CustomerID)
)
CREATE TABLE News (
    NewsID      int identity(1,1) not null,
    CreateUTC   datetime2(7) not null,
    Name        varchar(100) not null
    CONSTRAINT  PK_News Primary Key CLUSTERED (NewsID)
)

CREATE TABLE Alert (
    AlertID     int identity(1,1) not null,
    CreateUTC   datetime2(7) not null,
    ProductID   int null,
    NewsID      int null,
    CustomerID  int null,
    CONSTRAINT  PK_Alert Primary Key CLUSTERED (AlertID)
)

ALTER TABLE Alert WITH CHECK ADD CONSTRAINT CK_OnlyOneFKAllowed 
CHECK ( 
    (ProductID is not null AND NewsID is     null and CustomerID is     null) OR 
    (ProductID is     null AND NewsID is not null and CustomerID is     null) OR 
    (ProductID is     null AND NewsID is     null and CustomerID is not null) 
)

Solusi 2 - Satu FK di setiap Tabel Induk
Dalam solusi ini setiap tabel 'induk' memiliki FK ke tabel Alert. Itu membuatnya mudah untuk mengambil peringatan yang terkait dengan orang tua. Di sisi bawah, tidak ada rantai nyata dari Siaga ke siapa referensi. Lebih lanjut, model data memungkinkan untuk Peringatan yatim - di mana peringatan tidak terkait dengan Produk, Berita, atau Pelanggan. Sekali lagi, beberapa KIRI BERGABUNG untuk mencari tahu asosiasi.

CREATE TABLE Product (
    ProductID    int identity(1,1) not null,
    CreateUTC    datetime2(7) not null,
     Name        varchar(100) not null
    AlertID     int null,
    CONSTRAINT   PK_Product Primary Key CLUSTERED (ProductID)
)
CREATE TABLE Customer (
    CustomerID  int identity(1,1) not null,
    CreateUTC   datetime2(7) not null,
     Name       varchar(100) not null
    AlertID     int null,
    CONSTRAINT  PK_Customer Primary Key CLUSTERED (CustomerID)
)
CREATE TABLE News (
    NewsID      int identity(1,1) not null,
    CreateUTC   datetime2(7) not null,
    Name        varchar(100) not null
    AlertID     int null,
    CONSTRAINT  PK_News Primary Key CLUSTERED (NewsID)
)

CREATE TABLE Alert (
    AlertID     int identity(1,1) not null,
    CreateUTC   datetime2(7) not null,
    CONSTRAINT  PK_Alert Primary Key CLUSTERED (AlertID)
)

Apakah ini hanya kehidupan dalam database relasi? Apakah ada solusi alternatif yang menurut Anda lebih memuaskan?

EBarr
sumber
1
Bisakah Anda membuat satu tabel induk, Alertable, dengan kolom berikut: ID, CreateDate, Name, Type. Anda dapat meminta tiga meja anak FK untuk itu, dan Lansiran Anda akan FK hanya satu meja, Dapat Diperingatkan.
AK
Untuk beberapa kasus Anda membuat poin yang baik - secara efektif solusi # 3. Namun, kapan pun data bergerak cepat atau terlokalisasi, data itu tidak akan berfungsi. Misalnya, Produk, Pelanggan, dan Berita masing-masing memiliki tabel "Lang" yang sesuai untuk mendukung Nama dalam beberapa bahasa. Jika saya harus memberikan 'nama' dalam bahasa ibu pengguna, saya tidak dapat menyimpannya Alertable. Itu masuk akal?
EBarr
1
@EBarr: "Jika saya harus mengirimkan 'nama' dalam bahasa asli pengguna, saya tidak bisa menyimpannya di Alertable. Apakah itu masuk akal?" Tidak, tidak. Dengan skema Anda saat ini, jika Anda harus memberikan 'nama' dalam bahasa asli pengguna, dapatkah Anda menyimpannya di tabel Product, Customer atau News?
ypercubeᵀᴹ
@ypercube Saya menambahkan bahwa masing-masing tabel memiliki tabel bahasa yang terkait. Saya mencoba membuat skenario di mana teks "nama" dapat bervariasi per permintaan dan karenanya tidak dapat disimpan di Alertable.
EBarr
Kecuali jika sudah memiliki nama yang diterima, saya mengusulkan istilah "gurita bergabung" untuk kueri yang akan Anda lakukan untuk melihat semua lansiran dan orang tua terkait mereka. :)
Nathan Long

Jawaban:

4

Saya memahami solusi kedua yang tidak berlaku karena tidak menawarkan satu (objek) -untuk-banyak (peringatan) hubungan.

Anda terjebak hanya pada dua solusi karena kepatuhan 3NF yang ketat.

Saya akan merancang skema kopling yang lebih rendah:

CREATE TABLE Product  (ProductID  int identity(1,1) not null, ...)
CREATE TABLE Customer (CustomerID int identity(1,1) not null, ...)
CREATE TABLE News     (NewsID     int identity(1,1) not null, ...)

CREATE TABLE Alert (
  -- See (1)
  -- AlertID     int identity(1,1) not null,

  AlertClass char(1) not null, -- 'P' - Product, 'C' - Customer, 'N' - News
  ForeignKey int not null,
  CreateUTC  datetime2(7) not null,

  -- See (2)
  CONSTRAINT  PK_Alert Primary Key CLUSTERED (AlertClass, ForeignKey)
)

-- (1) you don't need to specify an ID 'just because'. If it's meaningless, just don't.
-- (2) I do believe in composite keys

Atau, jika hubungan integritas wajib, saya dapat merancang:

CREATE TABLE Product  (ProductID  int identity(1,1) not null, ...)
CREATE TABLE Customer (CustomerID int identity(1,1) not null, ...)
CREATE TABLE News     (NewsID     int identity(1,1) not null, ...)

CREATE TABLE Alert (
  AlertID     int identity(1,1) not null,
  AlertClass char(1) not null, /* 'P' - Product, 'C' - Customer, 'N' - News */
  CreateUTC  datetime2(7) not null,
  CONSTRAINT  PK_Alert Primary Key CLUSTERED (AlertID)
)

CREATE TABLE AlertProduct  (AlertID..., ProductID...,  CONSTRAINT FK_AlertProduct_X_Product(ProductID)    REFERENCES Product)
CREATE TABLE AlertCustomer (AlertID..., CustomerID..., CONSTRAINT FK_AlertCustomer_X_Customer(CustomerID) REFERENCES Customer)
CREATE TABLE AlertNews     (AlertID..., NewsID...,     CONSTRAINT FK_AlertNews_X_News(NewsID)             REFERENCES News)

Bagaimanapun...

Tiga solusi yang valid plus yang lain untuk dipertimbangkan untuk banyak (objek) -untuk satu (peringatan) hubungan ...

Ini disajikan, apa moralnya?

Mereka berbeda secara halus, dan bobot yang sama pada kriteria:

  • kinerja pada penyisipan dan pembaruan
  • kompleksitas pada permintaan
  • ruang penyimpanan

Jadi, pilih comfier itu untuk Anda.

Marcus Vinicius Pompeu
sumber
1
Terima kasih atas masukannya; Saya setuju dengan sebagian besar komentar Anda. Saya mungkin dengan cerdik menggambarkan hubungan yang diperlukan (tidak termasuk solusi 2 di mata Anda), karena itu adalah contoh yang dibuat-buat. fn1 - mengerti, saya hanya menyederhanakan tabel untuk fokus pada masalah. fn2 - kunci komposit dan saya kembali! Skema kopling yang lebih rendah Saya mengerti kesederhanaan, tetapi secara pribadi mencoba untuk merancang dengan DRI sedapat mungkin.
EBarr
Meninjau hal ini, saya mulai meragukan kebenaran solusi saya ... lagi pula, sudah terpilih dua kali, yang saya ucapkan terima kasih. Walaupun saya percaya desain saya valid tetapi tidak cocok untuk masalah yang diberikan, karena 'n' serikat pekerja / joins tidak ditangani ...
Marcus Vinicius Pompeu
Akronim DRI menangkap saya. Untuk Anda semua, ini adalah singkatan dari Declarative Referential Integrity, teknik di balik integritas data referensial yang umumnya diimplementasikan sebagai ... (drum roll) ... DDL ASING pernyataan KUNCI. Lebih lanjut tentang en.wikipedia.org/wiki/Declarative_Referential_Integrity dan msdn.microsoft.com/en-us/library/…
Marcus Vinicius Pompeu
1

Saya telah menggunakan tabel bergabung yang dipicu-dipicu. solusi berfungsi dengan baik sebagai pilihan terakhir jika refactoring db tidak mungkin atau diinginkan. Idenya adalah bahwa Anda memiliki tabel yang hanya ada untuk menangani masalah RI, dan semua DRI menentangnya.

Chris Travers
sumber