Шаблон дизайну - одна з багатьох батьківських таблиць


13

Я досить часто зустрічаю ситуацію в базі даних, коли дана таблиця може ФК перетворюватися на одну з кількох різних батьківських таблиць. Я бачив два рішення проблеми, але жоден не задовольняє особисто. Мені цікаво, які ще візерунки ви бачили там? Чи є кращий спосіб це зробити?

Надуманий приклад
Скажімо, моя система Alerts. Сповіщення можна отримувати для різних об'єктів - клієнтів, новин та продуктів. Подане сповіщення може бути для одного і лише одного предмета. З будь-якої причини Клієнти, статті та продукти швидко рухаються (або локалізуються), тому необхідний текст / дані не можна втягувати в Сповіщення при створенні сповіщення. Враховуючи це налаштування, я бачив два рішення.

Примітка. Нижче розміщено DDL для SQL Server, але моє запитання має стосуватися будь-яких СУБД.

Рішення 1 - Кілька нульових FKeys

У цьому рішенні таблиця, що посилається на одну із багатьох таблиць, має кілька стовпців FK (задля стислості, нижче DDL не показує створення FK). ДОБРО - У цьому рішенні приємно, що у мене є іноземні ключі. Нульова оптимічність ФК робить це зручним і відносно простим додаванням точних даних. Запит BAD не є великим, оскільки для отримання супутніх даних потрібно N LEFT JOINS або N UNION заяв. У SQL Server, зокрема, LEFT JOINS виключає створення індексованого виду.

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

Рішення 2 - по одному ФК у кожній батьківській таблиці
У цьому рішенні кожна «батьківська» таблиця має ФК до таблиці сповіщень. Це дозволяє легко отримувати сповіщення, пов’язані з батьком. З нижньої сторони немає реального ланцюга від Попередження до того, хто посилається. Крім того, модель даних передбачає можливість осиротіти оповіщення - де попередження не пов’язане з товаром, новинами чи замовником. Знову ж таки, декілька лівих приєднується, щоб з’ясувати асоціацію.

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

Це просто життя в базі даних про стосунки? Чи є альтернативні рішення, які ви знайшли більш задоволеними?


1
Чи можете ви створити одну батьківську таблицю, "Попереджувальна" із такими стовпцями: ідентифікатор, CreateDate, ім'я, тип. Ви можете мати свій тридітний стіл FK до нього, і ваш ALert буде ФК лише до одного столу, Попереджувальний.
АК

У деяких випадках ви добре зазначаєте - ефективне рішення №3. Щоразу, коли дані швидко рухаються або локалізуються, однак вони не працюватимуть. Скажімо, наприклад, що продукт, клієнт та новини мають відповідну таблицю "Lang", яка підтримує ім'я на кількох мовах. Якщо мені доведеться вводити "ім'я" рідною мовою користувачів, я не можу його зберігати Alertable. Це має сенс?
EBarr

1
@EBarr: "Якщо мені доведеться вводити " ім'я "на рідній мові користувачів, я не можу зберігати його в" Попереджувальному ". Це має сенс?" Ні, це не так. Якщо у вашій поточній схемі вам доведеться вводити "ім'я" рідною мовою користувачів, чи можете ви зберігати його в таблиці продуктів, клієнтів чи новин?
ypercubeᵀᴹ

@ypercube Я додав, що для кожної з цих таблиць є відповідна таблиця мови. Я намагався створити сценарій, коли текст "ім'я" може змінюватись в залежності від запиту, і тому його не можна зберігати в "Попереджуваному".
EBarr

Якщо у нього вже немає прийнятого імені, я пропоную термін "приєднатися до восьминога" для запиту, який ви повинні зробити, щоб побачити всі сповіщення та їх пов’язаних батьків. :)
Натан Лонг

Відповіді:


4

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

Ви дотримуєтесь лише двох рішень через суворе дотримання 3NF.

Я б створив меншу схему зв'язку:

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

Або, якщо відносини цілісності будуть обов'язковими, я можу створити:

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)

Все одно ...

Три дійсні рішення плюс ще одне, яке слід врахувати для багатьох (об'єктів) -до одного (тривожних) відносин ...

Ці представлені, яка мораль?

Вони відрізняються тонко і важать однаково за критеріями:

  • продуктивність при вставках та оновленнях
  • складність у запиті
  • місце для зберігання

Отож, вибирайте той комбінатор для вас.


1
Дякую за вклад; Я згоден з більшістю ваших коментарів. Я, мабуть, майстерно описав необхідні стосунки (виключаючи рішення 2 у ваших очах), як це був складений приклад. fn1 - зрозуміло, я просто спрощував таблиці, щоб зосередитися на проблемі. fn2 - складові ключі, і я повертаюся назад! Повторюючи схему з’єднання, я розумію простоту, але особисто намагаюся розробляти з DRI, де це можливо.
EBarr

Переглядаючи це, я почав сумніватися у правильності свого рішення ... все одно, його проголосували двічі, за що я дякую. Хоча я вважаю, що моя конструкція є дійсною, але не підходить для заданої проблеми, оскільки 'n' об'єднання / приєднання не розглядаються ...
Маркус Вініцій Помпеу

Акронім DRI отримав мене. Для всіх вас це означає Декларативну референтну цілісність, техніку, що стоїть за цілісністю референтних даних, яка зазвичай реалізовується як ... (барабан ролика) ... DDL FOREIGN KEY. Детальніше про en.wikipedia.org/wiki/Declarative_Referential_Integrity та msdn.microsoft.com/en-us/library/…
Маркус Вініцій Помпеу

1

Я використовував підтримувані тригером таблиці з'єднання. рішення працює досить добре в крайньому випадку, якщо рефакторинг db неможливий або бажаний. Ідея полягає в тому, що у вас є таблиця, яка є просто для вирішення проблем з RI, і всі DRI проти цього.

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