самостійні посилання на таблиці, добре чи погано? [зачинено]


37

Представляючи географічне розташування в рамках програми, дизайн основної моделі даних пропонує два чіткі варіанти (а може і більше?).

Одна таблиця з стовпцем з власним посиланням parent_id стовпчик uk - london (london parent id = UK id)

або дві таблиці, із співвідношенням один до багатьох за допомогою іноземного ключа.

Моя перевага - одна таблиця, що самостійно посилається, оскільки вона дозволяє легко поширитись на стільки підрегіонів, скільки потрібно.

Взагалі, люди відхиляються від таблиць, що самостійно посилаються, або вони A-OK?

Відповіді:


40

Нічого поганого в таблицях, що самостійно посилаються.

Це загальна модель дизайну баз даних для глибоко (нескінченно?) Вкладених ієрархій.


@NimChimpsky - Як і поняття рекурсії, для деяких ця ідея складна.
Одід

2
(Принаймні) Oracle навіть має спеціальну програму SQL, пункт "НАЧАЙТЕ З - ПІДКЛЮЧИТИ З", щоб мати справу з таблицями, що самостійно посилаються.
користувач281377

1
@ user281377 - і SQL Server представив hierarchyidтип.
Одід

увімкніть сплячку, щоб у неї був власний особливий соус
NimChimpsky

4
@NimChimpsky - Розгляньте розгляд "Вкладеної набірної моделі" як альтернативу стовпцю "parent_id" - це забезпечує таку ж функціональність, але кращу ефективність та простіші запити для вилучення ієрархій. en.wikipedia.org/wiki/Nested_set_model У книжковому серіалі Джо Селко "SQL For Smarties" є чудовий зразок SQL щодо вкладених наборів.
Кіт Палмер-молодший

7

Некромантування.
Правильна відповідь: це залежить від того, який двигун бази даних та який інструмент управління.

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

Найпростіший приклад стандартного рекурсивного відносини, як і будь-яка суть, що посилається на себе, ієрархія.

Отримана таблиця SQL-сервера:

IF  EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'dbo.FK_T_FMS_Reports_T_FMS_Reports') AND parent_object_id = OBJECT_ID(N'dbo.T_FMS_Reports'))
ALTER TABLE dbo.T_FMS_Reports DROP CONSTRAINT FK_T_FMS_Reports_T_FMS_Reports
GO

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'dbo.T_FMS_Reports') AND type in (N'U'))
DROP TABLE dbo.T_FMS_Reports 
GO



CREATE TABLE dbo.T_FMS_Reports 
( 
     RE_UID uniqueidentifier NOT NULL 
    ,RE_RE_UID uniqueidentifier NULL 
    ,RE_Text nvarchar(255) NULL 
    ,RE_Link nvarchar(400) NULL 
    ,RE_Sort int NOT NULL 
    ,RE_Status int NOT NULL 
    ,PRIMARY KEY CLUSTERED ( RE_UID ) 
); 

GO

ALTER TABLE dbo.T_FMS_Reports  WITH CHECK ADD  CONSTRAINT FK_T_FMS_Reports_T_FMS_Reports FOREIGN KEY(RE_RE_UID) 
REFERENCES dbo.T_FMS_Reports (RE_UID) 
-- ON DELETE CASCADE -- here, MS-SQL has a problem 
GO

ALTER TABLE dbo.T_FMS_Reports CHECK CONSTRAINT FK_T_FMS_Reports_T_FMS_Reports 
GO

Але у вас виникає проблема:
Коли вам потрібно видалити точку меню з усіма її підменю, ви не можете встановити каскад delete, оскільки Microsoft SQL-сервер не підтримує рекурсивні каскадні видалення (з іншого боку, PostGreSQL робить [але тільки якщо графік не циклічний], тоді як MySQL взагалі не любить подібну структуру таблиці, оскільки вона не підтримує рекурсивні CTE).

Таким чином, ви начебто підірвали цілісність / функціональність видалення з ним, зробивши обов'язковим впровадження такої функціональності у власному коді або у збереженій процедурі (якщо ваш RDBMS підтримує збережені процедури).

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

Наприклад, коли ви створюєте скрипт INSERT за допомогою SSMS, то SSMS не отримуватиме зовнішній ключ, і, таким чином, дійсно створює вкладиші-заяви, які будуть вставляти записи з залежностями, перш ніж він вставить батьківський зв'язок залежності, який вийде з ладу з помилкою , тому що сторонній ключ є на місці.

Однак у належних системах управління базами даних (на зразок PostgreSQL), при належному інструменті це не повинно бути проблемою. Просто відмовляйтесь від того, що ви просто платите за свої RDBMS (я дивлюся на вас, Microsoft; Oracle =?) Та / або його пояс, це не означає, що він правильно запрограмований. І OpenSource (наприклад, MySQL) не змушує вас не сприйняти таких чудових дрібниць.

Диявол у деталях, як йдеться у старому вислові.

Тепер не те, що ви не могли вирішити подібні проблеми, але я дійсно не рекомендую цього, якщо ваша система буде складною (наприклад, 200+ таблиць).
Крім того, у звичайній комерційній обстановці (як зображує Ділберт) вам просто не дадуть цього часу.

Набагато кращим підходом, хоча і складніше, буде таблиця закриття.
Це було б додатковим бонусом, що він також працює на MySQL.
Після того, як ви реалізуєте функцію закриття один раз, ви змусите його працювати в додаткових місцях майже взагалі.


3
+1 про те, щоб мені привернути до уваги таблиці закриття (принаймні термінологію, концепцію вже знав). Ось хороша стаття з цього приводу для інших, хто може зацікавити. coderwall.com/p/lixing/closure-tables-for-browsing-trees-in-sql
Вичерпаний Джерело

1

Це гарна ідея, якщо відносини насправді є ієрархічними, а не мережевими відносинами (наприклад, Біблійний матеріал - це мережевий зв'язок, а не ієрархічний).

Запит може бути повільним. Для прискорення роботи можна скористатися таблицею закриття.

http://karwin.blogspot.ca/2010/03/rendering-trees-with-closure-tables.html

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