таблицы со ссылками, хорошие или плохие? [закрыто]

37

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

Одна таблица с самоссылающейся колонкой parent_id uk - london (london parent id = UK id)

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

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

В общем, люди отклоняются от таблиц, на которые ссылаются сами, или они в порядке?

ним чимпский
источник

Ответы:

40

Ничего плохого в таблицах с самообращением.

Это общий шаблон проектирования базы данных для глубоко (бесконечности?) Вложенных иерархий.

Одед
источник
@NimChimpsky - Как и концепция рекурсии, эта идея сложна для некоторых.
Одед
2
(По крайней мере) Oracle даже имеет специальную конструкцию SQL, условие «START WITH - CONNECT BY», для работы с таблицами с самоссылкой.
user281377
1
@ user281377 - И SQL Server ввел hierarchyidтип.
Одед
Используй hibernate, чтобы у него был свой особый соус
NimChimpsky
4
@NimChimpsky - Рассмотрите вариант «Модель вложенного набора» как альтернативу этому столбцу «parent_id» - он предоставляет те же функциональные возможности, но повышает производительность и упрощает запросы для извлечения иерархий. en.wikipedia.org/wiki/Nested_set_model В серии книг Джо Селко «SQL для умных» приведен отличный пример SQL, касающийся вложенных множеств.
Кит Палмер младший
7

Necromancing.
Правильный ответ: это зависит от того, какой механизм базы данных и какой инструмент управления.

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

Самый простой пример стандартного рекурсивного отношения, как и для любой самоссылающейся сущности / иерархии.

В результате получается таблица SQL-Server:

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-cascade, потому что Microsoft SQL-Server не поддерживает рекурсивное каскадное удаление (с другой стороны, PostGreSQL делает [но только если graph не циклический], в то время как MySQL вообще не нравится такая структура таблиц, потому что она не поддерживает рекурсивные CTE).

Таким образом, вы как бы взрывают целостность / функциональность удаления, делая обязательным выполнение таких функций в своем собственном коде или в хранимой процедуре (если ваша СУБД поддерживает хранимые процедуры).

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

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

Тем не менее, в надлежащих системах управления базами данных (таких как PostgreSQL), с надлежащим инструментарием, это не должно быть проблемой. Просто поймите, что если вы много платите за свою СУБД (я смотрю на вас, Microsoft; Oracle =?) И / или на ее пояс для инструментов, это не значит, что она запрограммирована правильно. И при этом OpenSource (например, MySQL) не делает вас невосприимчивым к таким замечательным мелочам.

Дьявол кроется в деталях, как гласит старая поговорка.

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

Гораздо лучшим подходом, хотя и более сложным, будет закрытие таблицы.
Это будет иметь дополнительный бонус, что он также работает на MySQL.
Как только вы реализуете функцию замыкания один раз, она будет работать в дополнительных местах практически за короткое время.

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

Это хорошая идея, если отношения на самом деле иерархические, а не сетевые (например, спецификация - это сетевые, а не иерархические).

Это может быть медленным, чтобы запросить. Чтобы ускорить процесс, вы можете использовать таблицу закрытия.

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

Нил Макгиган
источник