Apakah ada DBMS yang memungkinkan Kunci Asing yang Referensi Lihat (dan bukan hanya tabel dasar)?

22

Terinspirasi oleh pertanyaan pemodelan Django: Pemodelan Basis Data dengan banyak hubungan banyak ke banyak di Django . Desain db adalah sesuatu seperti:

CREATE TABLE Book
( BookID INT NOT NULL
, BookTitle VARCHAR(200) NOT NULL
, PRIMARY KEY (BookID)
) ;

CREATE TABLE Tag
( TagID INT NOT NULL
, TagName VARCHAR(50) NOT NULL
, PRIMARY KEY (TagID)
) ;

CREATE TABLE BookTag
( BookID INT NOT NULL
, TagID INT NOT NULL
, PRIMARY KEY (BookID, TagID)
, FOREIGN KEY (BookID)  REFERENCES Book (BookID)
, FOREIGN KEY (TagID)   REFERENCES Tag (TagID)
) ;

CREATE TABLE Aspect
( AspectID INT NOT NULL
, AspectName VARCHAR(50) NOT NULL
, PRIMARY KEY (AspectID)
) ;

CREATE TABLE TagAspect
( TagID INT NOT NULL
, AspectID INT NOT NULL
, PRIMARY KEY (TagID, AspectID) 
, FOREIGN KEY (TagID)   REFERENCES Tag (TagID)
, FOREIGN KEY (AspectID)  REFERENCES Aspect (AspectID)
) ;

diagram db

dan masalahnya adalah bagaimana mendefinisikan BookAspectRatingtabel dan untuk menegakkan integritas referensial, jadi orang tidak dapat menambahkan peringkat untuk (Book, Aspect)kombinasi yang tidak valid.

AFAIK, CHECKkendala kompleks (atau ASSERTIONS) yang melibatkan subqueries dan lebih dari satu tabel, yang mungkin bisa menyelesaikan ini, tidak tersedia di DBMS.

Gagasan lain adalah menggunakan (pseudocode) tampilan:

CREATE VIEW BookAspect_view
  AS
SELECT DISTINCT
    bt.BookId
  , ta.AspectId
FROM 
    BookTag AS bt
  JOIN 
    Tag AS t  ON t.TagID = bt.TagID
  JOIN 
    TagAspect AS ta  ON ta.TagID = bt.TagID 
WITH PRIMARY KEY (BookId, AspectId) ;

dan tabel yang memiliki Kunci Asing untuk Tampilan di atas:

CREATE TABLE BookAspectRating
( BookID INT NOT NULL
, AspectID INT NOT NULL
, PersonID INT NOT NULL
, Rating INT NOT NULL
, PRIMARY KEY (BookID, AspectID, PersonID)
, FOREIGN KEY (PersonID)   REFERENCES Person (PersonID)
, FOREIGN KEY (BookID, AspectID) 
    REFERENCES BookAspect_view (BookID, AspectID)
) ;

Tiga pertanyaan:

  • Apakah ada DBMS yang memungkinkan (mungkin terwujud) VIEWdengan a PRIMARY KEY?

  • Apakah ada DBMS yang memungkinkan FOREIGN KEYbahwa REFERENCESsuatu VIEW(dan tidak hanya basa TABLE)?

  • Bisakah masalah integritas ini diselesaikan sebaliknya - dengan fitur-fitur DBMS yang tersedia?


Klarifikasi:

Karena mungkin tidak ada solusi yang memuaskan 100% - dan pertanyaan Django bahkan bukan milikku! - Saya lebih tertarik pada strategi umum kemungkinan serangan terhadap masalah, bukan solusi terperinci. Jadi, jawaban seperti "dalam DBMS-X ini dapat dilakukan dengan pemicu pada tabel A" dapat diterima.

ypercubeᵀᴹ
sumber
Posting sebagai komentar untuk dua pertanyaan pertama Anda - dan belum tentu untuk Anda, karena saya yakin Anda sudah tahu - tetapi SQL Server tidak mendukung kunci utama atau asing untuk tampilan.
Aaron Bertrand
@ Harun: ya, terima kasih. Saya telah membaca bahwa Oracle mendukung kendala biaya PK dalam pandangan. Tetapi tidak yakin apakah itu akan berhasil dalam situasi ini. Dan jawaban untuk pertanyaan 2 (tentang FK untuk dilihat) mungkin negatif di Oracle.
ypercubeᵀᴹ
Tapi saya tertarik untuk mengetahui apakah ada solusi lain (pemicu, periksa kendala biaya atau kombo lainnya)
ypercubeᵀᴹ

Jawaban:

9

Aturan bisnis ini dapat ditegakkan dalam model hanya menggunakan kendala. Tabel berikut ini akan menyelesaikan masalah Anda. Gunakan itu sebagai ganti tampilan Anda:

    CREATE TABLE BookAspectCommonTagLink
    (  BookID INT NOT NULL
    , AspectID INT NOT NULL
    , TagID INT NOT NULL
--TagID is deliberately left out of PK
    , PRIMARY KEY (BookID, AspectID)
    , FOREIGN KEY (BookID, TagID) 
        REFERENCES BookTag (BookID, TagID)
    , FOREIGN KEY (AspectID, TagID) 
        REFERENCES AspectTag (AspectID, TagID)
    ) ;
AK
sumber
Oh bagus. Satu-satunya masalah yang bisa saya pikirkan adalah kompleksitas yang diperkenalkan dalam menyisipkan / menghapus BookTags dan TagAspects. Setiap kali BookTag baru (atau TagAspect) dihapus misalnya, pencarian harus dilakukan untuk menghapus baris yang sesuai dalam tabel ini dan / atau mengubah TagIDke Tag lain yang terkait dengan kombinasi BookAspect yang sama.
ypercubeᵀᴹ
Dan pencarian serupa harus dilakukan untuk memasukkan ke dalam 2 tabel tersebut. Tetapi aturan yang rumit membutuhkan prosedur yang rumit, jadi ini terlihat sangat bagus.
ypercubeᵀᴹ
@ypercube Ketika Anda menghapus tag, Anda perlu memeriksa dan mungkin beralih ke tag lain yang menghubungkan Buku dan Aspek yang sama. Ketika Anda memasukkan tag baru, bagaimanapun, tidak perlu melakukan pemeriksaan sampai Anda perlu memasukkan peringkat.
AK
1
Jika pemecah masalah dan orang entri data adalah orang yang sama, atau jika Anda mengekspos pesan kesalahan kepada pengguna akhir, tentu saja. Anda terlalu memikirkan toko satu orang tempat Anda melakukan semuanya.
Aaron Bertrand
4
@ AaronBertrand Kau baru saja membantuku. Saya menyelesaikan sebuah artikel berjudul "Mengembangkan Basis Data Perawatan Rendah", dan saya lupa menyebutkan bahwa server aplikasi harus mencatat pesan kesalahan asli yang berasal dari basis data. Saya baru saja menambahkannya. Terima kasih telah mengingatkan;)
AK
8

Saya pikir Anda akan menemukan bahwa dalam banyak kasus, aturan bisnis yang kompleks tidak dapat ditegakkan hanya melalui model. Ini adalah salah satu kasus di mana, setidaknya dalam SQL Server, saya pikir pemicu (lebih baik daripada pemicu) lebih baik melayani tujuan Anda.

Aaron Bertrand
sumber
Hei Aaron, bisakah Anda jelaskan mengapa dalam hal ini pemicu adalah pilihan yang lebih baik daripada entitas dan beberapa kendala?
AK
2
@AlexKuznetsov Tentu, karena saya tidak menghabiskan 17 jam memikirkan bagaimana menerapkan ini dengan beberapa kunci asing multi-kolom, dan semua logika tambahan yang mungkin diperlukan untuk menangani validasi dan penanganan kesalahan?
Aaron Bertrand
2
Berhati-hatilah dengan kondisi ras yang mungkin diterapkan oleh pemicu yang naif. Misalnya, satu transaksi mungkin memutuskan buku dari tag dan yang lain masih berpikir itu OK untuk terhubung ke aspek yang sesuai, hanya karena transaksi pertama belum dilakukan. Kompleksitas yang diperkenalkan oleh jawaban @AlexKuznetsov mungkin kurang dari kompleksitas dan kerapuhan dari "protokol" penguncian yang diperlukan untuk mencegah kondisi balapan di pemicu, IMHO.
Branko Dimitrijevic
8

Di Oracle, salah satu cara untuk menegakkan batasan semacam ini secara deklaratif adalah dengan membuat tampilan terwujud yang diatur untuk menyegarkan cepat pada komit yang kueri mengidentifikasi semua baris yang tidak valid (yaitu BookAspectRatingbaris yang tidak cocok dengan BookAspect_view). Anda kemudian dapat membuat batasan sepele pada tampilan terwujud yang akan dilanggar jika ada baris dalam tampilan terwujud. Ini memiliki manfaat meminimalkan jumlah data yang harus Anda duplikasi dalam tampilan terwujud. Namun, hal itu dapat menyebabkan masalah, karena kendala hanya ditegakkan pada saat Anda melakukan transaksi - banyak aplikasi tidak ditulis untuk mengharapkan bahwa operasi komit mungkin gagal - dan karena pelanggaran kendala bisa agak sulit untuk mengasosiasikan dengan baris tertentu atau tabel tertentu.

Gua Justin
sumber
4

SIRA_PRISE memungkinkan itu.

Meskipun FK tidak disebut "FK" lagi, tetapi hanya "batasan basis data", dan "tampilan" sebenarnya tidak harus didefinisikan sebagai tampilan, Anda hanya bisa memasukkan ekspresi definisi tampilan di dalam deklarasi the kendala basis data.

Batasan Anda akan terlihat seperti

SEMIMINUS(BOOKASPECT , JOIN(BOOKTAG , TAGASPECT))

dan kamu sudah selesai.

Namun dalam sebagian besar DBMS SQL, Anda harus melakukan analisis pada kendala Anda, menentukan bagaimana hal itu dapat dilanggar dan mengimplementasikan semua pemicu yang diperlukan.

Erwin Smout
sumber
Aku tahu. Itu mencerminkan apa yang saya pikir penting pada saat penulisan.
Erwin Smout
3

Dalam PostgreSQL, saya tidak dapat membayangkan solusi tanpa melibatkan pemicu, tetapi tentu saja dapat dipecahkan dengan cara itu (baik itu mempertahankan pandangan terwujud semacam atau sebelum pemicu aktif BookAspectRating). Tidak ada kunci asing yang mereferensikan tampilan ( ERROR: referenced relation "v_munkalap" is not a table), apalagi kunci primer.

dezso
sumber