มี DBMS ที่อนุญาตให้ใช้ Foreign Key ที่อ้างอิงมุมมอง (และไม่เพียง แต่ตารางพื้นฐาน)?


22

แรงบันดาลใจจากการสร้างแบบจำลองคำถาม Django: การสร้างแบบจำลองฐานข้อมูลที่มีความสัมพันธ์หลายต่อหลายหลายใน Django การออกแบบ db เป็นสิ่งที่ชอบ:

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)
) ;

ไดอะแกรม db

และปัญหาคือวิธีการกำหนดBookAspectRatingตารางและการบังคับใช้ Referential Integrity ดังนั้นจึงไม่สามารถเพิ่มการจัดอันดับสำหรับ(Book, Aspect)ชุดค่าผสมที่ไม่ถูกต้อง

AFAIK CHECKข้อ จำกัดที่ซับซ้อน(หรือASSERTIONS) ที่เกี่ยวข้องกับแบบสอบถามย่อยและมากกว่าหนึ่งตารางที่อาจเป็นไปได้ที่จะแก้ปัญหานี้ไม่สามารถใช้ได้ใน DBMS ใด ๆ

อีกแนวคิดหนึ่งคือใช้ (pseudocode) มุมมอง:

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) ;

และตารางที่มี Foreign Key ไปที่มุมมองด้านบน:

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)
) ;

คำถามสามข้อ:

  • มี DBMS ที่อนุญาต (อาจปรากฏขึ้น) VIEWด้วยPRIMARY KEYหรือไม่

  • มี DBMS ที่ช่วยให้FOREIGN KEYว่า(และไม่เพียงฐาน)?REFERENCESVIEWTABLE

  • ปัญหาความสมบูรณ์นี้สามารถแก้ไขได้เป็นอย่างอื่น - ด้วยคุณสมบัติ DBMS ที่มีอยู่หรือไม่


ชี้แจง:

เนื่องจากอาจไม่มีวิธีการแก้ปัญหาที่น่าพึงพอใจ 100% - และคำถาม Django ก็ไม่ได้เป็นของฉัน! - ฉันสนใจกลยุทธ์ทั่วไปของการโจมตีที่เป็นไปได้ของปัญหาไม่ใช่วิธีแก้ปัญหาอย่างละเอียด ดังนั้นคำตอบเช่น"ใน DBMS-X สามารถทำได้ด้วยทริกเกอร์ในตาราง A"จึงเป็นที่ยอมรับอย่างสมบูรณ์


การโพสต์เป็นความคิดเห็นสำหรับคำถามสองข้อแรกของคุณ - และไม่จำเป็นสำหรับคุณเพราะฉันแน่ใจว่าคุณทราบแล้ว - แต่ SQL Server ไม่สนับสนุนคีย์หลักหรือคีย์ต่างประเทศสำหรับมุมมอง
Aaron Bertrand

@Aaron: ใช่ขอบคุณ ฉันอ่านแล้วว่า Oracle รองรับการใช้งาน PK ในมุมมองต่างๆ แต่ไม่แน่ใจว่ามันจะทำงานในสถานการณ์นี้หรือไม่ และคำตอบสำหรับคำถามที่ 2 (เกี่ยวกับมุมมอง FKs) น่าจะเป็นเชิงลบใน Oracle
ypercubeᵀᴹ

แต่ผมสนใจที่จะเรียนรู้ว่ามีวิธีอื่น ๆ (เรียกตรวจสอบ costraints หรือคำสั่งผสมอื่น ๆ )
ypercubeᵀᴹ

คำตอบ:


9

กฎธุรกิจนี้สามารถบังคับใช้ในโมเดลโดยใช้ข้อ จำกัด เท่านั้น ตารางต่อไปนี้ควรแก้ปัญหาของคุณ ใช้มันแทนมุมมองของคุณ:

    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)
    ) ;

ดี. ปัญหาเดียวที่ฉันคิดได้ก็คือความซับซ้อนที่นำมาใช้ในการแทรก / ลบ BookTags และ TagAspects ทุกครั้งที่ลบ BookTag ใหม่ (หรือ TagAspect) ออกจะต้องทำการค้นหาเพื่อลบแถวที่เกี่ยวข้องในตารางนี้และ / หรือเปลี่ยนเป็นTagIDแท็กอื่นที่เกี่ยวข้องกับชุดหนังสือ BookAspect เดียวกัน
ypercubeᵀᴹ

และการค้นหาที่คล้ายกันจะต้องทำเพื่อแทรกลงในตารางที่ 2 แต่กฎที่ซับซ้อนต้องใช้ขั้นตอนที่ซับซ้อนดังนั้นนี่จึงดูดีจริงๆ
ypercubeᵀᴹ

@ypercube เมื่อคุณลบแท็กคุณจะต้องตรวจสอบและอาจเปลี่ยนเป็นแท็กอื่นที่เชื่อมโยง Book and Aspect เดียวกัน อย่างไรก็ตามเมื่อคุณแทรกแท็กใหม่คุณไม่จำเป็นต้องทำการตรวจสอบใด ๆ จนกว่าคุณจะต้องใส่คะแนน
AK

1
หากตัวแก้ไขปัญหาและผู้ป้อนข้อมูลเป็นบุคคลเดียวกันหรือถ้าคุณเปิดเผยข้อความแสดงข้อผิดพลาดแก่ผู้ใช้ คุณกำลังคิดมากเกี่ยวกับร้านค้าหนึ่งคนที่คุณทำทุกอย่าง
Aaron Bertrand

4
@AaronBertrand คุณแค่ทำฉันโปรดปรานมาก ฉันกำลังเขียนบทความเรื่อง "การพัฒนาฐานข้อมูลการบำรุงรักษาต่ำ" และฉันลืมที่จะพูดถึงว่าเซิร์ฟเวอร์แอปต้องบันทึกข้อความแสดงข้อผิดพลาดดั้งเดิมที่มาจากฐานข้อมูล ฉันเพิ่งเพิ่มมัน ขอบคุณสำหรับการเตือน;)
AK

8

ฉันคิดว่าคุณจะพบว่าในหลายกรณีกฎเกณฑ์ทางธุรกิจที่ซับซ้อนไม่สามารถบังคับใช้ผ่านโมเดลเพียงอย่างเดียว นี่เป็นหนึ่งในกรณีที่อย่างน้อยใน SQL Server ฉันคิดว่าทริกเกอร์ (ควรจะเป็นทริกเกอร์แทน) ดีกว่าตอบสนองวัตถุประสงค์ของคุณ


เฮ้แอรอนคุณช่วยอธิบายหน่อยได้ไหมว่าทำไมในกรณีนี้ตัวกระตุ้นจึงเป็นตัวเลือกที่ดีกว่าเอนทิตีและข้อ จำกัด บางประการ?
AK

2
@AlexKuznetsov แน่นอนเพราะฉันไม่ได้ใช้เวลา 17 ชั่วโมงคิดเกี่ยวกับวิธีการใช้คีย์ต่างประเทศหลายคอลัมน์และตรรกะพิเศษทั้งหมดที่อาจต้องใช้ในการจัดการกับการตรวจสอบและการจัดการข้อผิดพลาด
Aaron Bertrand

2
ระมัดระวังเกี่ยวกับสภาพการแข่งขันที่อาจนำมาใช้ทริกเกอร์ไร้เดียงสา ตัวอย่างเช่นธุรกรรมหนึ่งอาจตัดการเชื่อมต่อหนังสือจากแท็กและอีกรายการหนึ่งยังคงคิดว่าเป็นการตกลงที่จะเชื่อมต่อกับแง่มุมที่เกี่ยวข้องเนื่องจากธุรกรรมแรกยังไม่ได้กระทำ ความซับซ้อนที่ได้รับการแนะนำโดย @AlexKuznetsov คำตอบอาจน้อยกว่าความซับซ้อนและความเปราะบางของการล็อค "โปรโตคอล" ที่จำเป็นในการป้องกันสภาวะการแข่งขันในทริกเกอร์ IMHO
Branko Dimitrijevic

8

ใน Oracle วิธีหนึ่งในการบังคับใช้ข้อ จำกัด แบบนี้ในการประกาศคือการสร้างมุมมองที่เป็นรูปธรรมซึ่งถูกตั้งค่าให้รีเฟรชอย่างรวดเร็วในการส่งมอบแบบสอบถามที่ระบุแถวที่ไม่ถูกต้องทั้งหมด (เช่นBookAspectRatingแถวที่ไม่มีข้อมูลตรงกันBookAspect_view) จากนั้นคุณสามารถสร้างข้อ จำกัด เล็กน้อยในมุมมองที่ปรากฏขึ้นซึ่งจะถูกละเมิดหากมีแถวใด ๆ ในมุมมองที่ปรากฏ สิ่งนี้มีประโยชน์ในการลดจำนวนข้อมูลที่คุณต้องทำซ้ำในมุมมองที่ปรากฏ มันอาจทำให้เกิดปัญหาได้เนื่องจากข้อ จำกัด นั้นบังคับใช้เฉพาะในจุดที่คุณกำลังทำธุรกรรม - แอปพลิเคชันจำนวนมากไม่ได้ถูกเขียนขึ้นเพื่อคาดหวังว่าการดำเนินการกระทำอาจล้มเหลว - และเนื่องจากการละเมิดข้อ จำกัด นั้นค่อนข้างยาก เพื่อเชื่อมโยงกับแถวใดตารางหนึ่งหรือตารางเฉพาะ


4

SIRA_PRISEยอมให้สิ่งนั้น

แม้ว่า FK จะไม่ถูกเรียกว่า "FK" อีกต่อไป แต่เป็นเพียงแค่ "ข้อ จำกัด ฐานข้อมูล" และ "มุมมอง" นั้นในความเป็นจริงไม่จำเป็นต้องกำหนดเป็นมุมมอง แต่คุณสามารถรวมมุมมองที่กำหนดนิพจน์ภายในการประกาศของ ข้อ จำกัด ของฐานข้อมูล

ข้อ จำกัด ของคุณจะมีลักษณะเช่นนี้

SEMIMINUS(BOOKASPECT , JOIN(BOOKTAG , TAGASPECT))

และคุณทำเสร็จแล้ว

อย่างไรก็ตามใน SQL DBMS ส่วนใหญ่คุณจะต้องทำการวิเคราะห์ด้วยข้อ จำกัด ของคุณพิจารณาว่าจะสามารถละเมิดและใช้ทริกเกอร์ที่จำเป็นทั้งหมดได้อย่างไร


ฉันรู้ว่า. มันสะท้อนให้เห็นถึงสิ่งที่ฉันคิดว่ามีความสำคัญในช่วงเวลาของการเขียน
Erwin Smout

3

ใน PostgreSQL, ฉันไม่สามารถจินตนาการการแก้ปัญหาโดยไม่เกี่ยวข้องกับการเรียก แต่อย่างแน่นอนจะสามารถแก้ไขได้ว่าวิธีการ (ไม่ว่าจะรักษา materialized ดูการจัดเรียงบางส่วนหรือก่อนที่จะทริกเกอร์บนBookAspectRating) ไม่มีคีย์ต่างประเทศที่อ้างอิงมุมมอง ( ERROR: referenced relation "v_munkalap" is not a table) ให้คีย์หลักเป็นหลัก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.