Чи є СУБД, які дозволяють зовнішній ключ, який посилається на перегляд (і не тільки базові таблиці)?


22

Натхненний питанням моделювання Джанго: Моделювання баз даних з безліччю відносин багато-багато-багато в Джанго . Дизайн 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таблицю та застосувати референтну цілісність, тому не можна додавати рейтинг для (Book, Aspect)недійсної комбінації.

AFAIK, складні CHECKобмеження (або ASSERTIONS), які містять підзапити та більше однієї таблиці, які могли б вирішити це, не доступні в жодній СУБД.

Інша ідея - використовувати (псевдокод) вид:

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

та таблиця із зовнішнім ключем до наведеного вище перегляду:

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

Три питання:

  • Чи є СУБД, які дозволяють (можливо, матеріалізуватися) VIEWз PRIMARY KEY?

  • Чи існують СУБД , які дозволяють , FOREIGN KEYщо REFERENCESв VIEW(а не тільки бази TABLE)?

  • Чи можна вирішити цю проблему цілісності інакше - з наявними функціями СУБД?


Пояснення:

Оскільки, мабуть, немає 100% задовольняючого рішення - і питання Джанго навіть не моє! - Мене більше цікавить загальна стратегія можливого нападу на проблему, а не детальне рішення. Отже, відповідь на кшталт "у СУБД-X це можна зробити за допомогою тригерів на таблиці А" цілком прийнятна.


Опублікувати як коментар до своїх перших двох запитань - і це не обов'язково для вас, як я впевнений, ви вже знаєте - але SQL Server не підтримує первинний або зовнішній ключі для переглядів.
Аарон Бертран

@Aaron: так, дякую Я читав, що Oracle підтримує обмеження ПК у видах. Але не впевнений, чи спрацювало б це в цій ситуації. І відповідь на друге запитання (щодо ФК на погляди), ймовірно, в Oracle негативний.
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 Коли ви видаляєте тег, вам потрібно перевірити та, можливо, перейти на інший тег, що пов'язує ту саму Книгу та Аспект. Однак, коли ви вставляєте нові теги, не потрібно робити жодних перевірок, поки вам не потрібно вставити рейтинг.
АК

1
Якщо засіб усунення несправностей та особа, що вводить дані, є тією ж особою, або якщо ви виставите повідомлення про помилку кінцевому користувачеві, обов'язково. Ви занадто багато думаєте про магазини для однієї людини, де ви все робите.
Аарон Бертран

4
@AaronBertrand Ви просто зробили мені величезну прихильність. Закінчую статтю під назвою "Розробка баз даних з низьким обслуговуванням", і я забув згадати, що сервери додатків повинні реєструвати оригінальні повідомлення про помилки, що надходять із баз даних. Я тільки що додав. Дякую за нагадування;)
АК

8

Я думаю, ви побачите, що у багатьох випадках складні правила бізнесу не можуть бути застосовані лише через модель. Це один із тих випадків, коли, принаймні, у SQL Server, я думаю, що тригер (бажано замість тригера) краще відповідає вашим цілям.


Ей, Аарон, чи можете ви пояснити, чому в цьому випадку тригер є кращим вибором, ніж сутність та кілька обмежень?
АК

2
@ Алекс Кузнецов Впевнений, бо я не витрачав 17 годин на роздуми над тим, як реалізувати це за допомогою декількох багатоколонних зовнішніх ключів, і всю додаткову логіку, яка може знадобитися для вирішення перевірки та обробки помилок у будь-якому випадку?
Аарон Бертран

2
Будьте обережні щодо перегонових умов, які можуть запровадити наївне реалізація тригерів. Наприклад, одна транзакція може від’єднати книгу від тегу, а інша все ще вважає, що підключається до відповідного аспекту, просто тому, що перша транзакція ще не здійснена Складнощі, введені у відповідь @AlexKuznetsov, ймовірно, менші, ніж складність та крихкість замикаючого "протоколу", необхідного для запобігання гоночним умовам в тригерах, IMHO.
Бранко Димитріевич

8

В Oracle одним із способів примусового застосування такого роду обмежень було б створити матеріалізований вигляд, який встановлюється для швидкого оновлення при виконанні, запит якого ідентифікує всі недійсні рядки (тобто BookAspectRatingрядки, які не відповідають BookAspect_view). Потім ви можете створити тривіальне обмеження для цього матеріалізованого представлення, яке було б порушено, якщо в матеріалізованому поданні є рядки. Це має перевагу мінімізувати кількість даних, які вам доведеться дублювати в матеріалізованому представленні. Однак це може спричинити проблеми, оскільки обмеження застосовується лише в тому випадку, коли ви здійснюєте транзакцію. Багато додатків не пишуться, щоб очікувати, що операція з фіксацією може вийти з ладу-- і тому, що порушення обмеження може бути дещо важким пов’язати з певним рядком або певною таблицею.


4

SIRA_PRISE дозволяє це.

Хоча FK більше не називається "FK", а просто "обмеження в базі даних", а "view" насправді навіть не потрібно визначати як перегляд, ви можете просто включити вигляд, що визначає вираз всередині декларації обмеження бази даних.

Ваше обмеження виглядало б приблизно так

SEMIMINUS(BOOKASPECT , JOIN(BOOKTAG , TAGASPECT))

і ви закінчили.

Однак у більшості SQL СУБД вам доведеться виконати аналіз роботи над обмеженням, визначити, як це може бути порушено та реалізувати всі необхідні тригери.


Я знаю. Це відображає те, що я вважав важливим під час написання.
Ервін Смоут

3

У PostgreSQL я не можу уявити рішення без залучення тригерів, але це, безумовно, може бути вирішено таким чином (будь то підтримання якогось матеріалізованого перегляду чи попереднього запуску BookAspectRating). Немає зовнішніх ключів, що посилаються на view ( ERROR: referenced relation "v_munkalap" is not a table), не кажучи вже про первинний ключ.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.