tables d'auto-référencement, bonnes ou mauvaises? [fermé]

37

Représentant des emplacements géographiques au sein d’une application, la conception du modèle de données sous-jacent suggère deux options claires (ou peut-être plus?).

Une table avec une colonne auto-référence parent_id uk - london (id parent london = id UK)

ou deux tables, avec une relation un à plusieurs en utilisant une clé étrangère.

Ma préférence va à une table d'auto-évaluation car elle permet de s'étendre facilement dans autant de sous-régions que nécessaire.

En général, les gens s'éloignent-ils des tables d'auto-référencement ou sont-ils OK?

NimChimpsky
la source

Réponses:

40

Rien à redire avec les tables à auto-référencement.

C'est le modèle de conception de base de données commun pour les hiérarchies imbriquées profondément (infinies?).

Oded
la source
@NimChimpsky - Tout comme le concept de récursion, cette idée est difficile pour certains.
Oded
2
(Au moins) Oracle a même une structure SQL spéciale, la clause "START WITH - CONNECT BY", pour gérer les tables à auto-référencement.
user281377
1
@ user281377 - Et SQL Server a introduit le hierarchyidtype.
Oded
Usign hiberner afin qu'il ait sa propre sauce spéciale
NimChimpsky
4
@NimChimpsky - Envisagez également d'utiliser le "modèle d'ensemble imbriqué" comme alternative à la colonne "id_ parent" - il offre les mêmes fonctionnalités, mais de meilleures performances et des requêtes plus faciles pour extraire les hiérarchies. en.wikipedia.org/wiki/Nested_set_model La série de livres "SQL For Smarties" de Joe Celko propose un excellent exemple de code SQL concernant les ensembles imbriqués.
Keith Palmer Jr.
7

Nécromancie.
La réponse correcte est: cela dépend de quel moteur de base de données et de quel outil de gestion.

Prenons un exemple:
nous avons une table de rapport,
et un rapport peut avoir un parent (menupoint, comme une catégorie),
et ce parent peut lui-même avoir un parent (par exemple, Centre de profit),
et ainsi de suite à l'infini.

L'exemple le plus simple d'une relation récursive standard, comme avec toute entité / hiérarchie à référence automatique.

La table SQL-Server résultante est:

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

Mais vous rencontrez un problème:
lorsque vous devez supprimer un point de menu avec tous ses sous-points, vous NE POUVEZ PAS définir delete-cascade, car Microsoft SQL-Server ne prend pas en charge les suppressions en cascade récursives (par contre, PostGreSQL ne [ graph n'est pas cyclique], alors que MySQL n'aime pas du tout ce type de structure de tableau, car il ne supporte pas les CTE récursifs).

Vous détruisez donc un peu l’intégrité / fonctionnalité de suppression, ce qui rend obligatoire l’implémentation de cette fonctionnalité dans votre propre code ou dans une procédure stockée (si votre SGBDR prend en charge les procédures stockées).

Cela entraînera sans aucun doute toute sorte d'importation / exportation de données dynamiques entièrement automatique, car vous ne pouvez pas simplement exécuter une instruction delete pour toutes les tables en fonction de relations de clé étrangère (sans auto-référencement), ni effectuer une simple sélection. * et créez un insert pour chaque ligne dans un ordre arbitraire.

Par exemple, lorsque vous créez un script INSERT à l'aide de SSMS, SSMS ne récupère pas la clé étrangère et crée donc bien des instructions d'insertion qui insèrent des entrées avec des dépendances, avant d'insérer le parent de la dépendance, ce qui échouera avec une erreur. , car la clé étrangère est en place.

Cependant, sur des systèmes de gestion de base de données appropriés (tels que PostgreSQL), avec un outillage approprié, cela ne devrait pas poser de problème. Comprenez simplement que le fait que vous payiez beaucoup pour votre SGBDR (je vous regarde, Microsoft; Oracle =?) Et / ou sa ceinture d’outils ne signifie pas qu’il est programmé correctement. Et OpenSource (par exemple, MySQL) ne vous rend pas non plus immunisé contre de telles minuties.

Le diable est dans les détails, comme le dit le vieil adage.

Maintenant, non pas que vous ne puissiez pas contourner de tels problèmes, mais je ne le recommanderais vraiment pas, si votre système devait être complexe (par exemple, plus de 200 tables).
De plus, dans un contexte commercial habituel (tel que décrit par Dilbert), on ne vous donnera tout simplement pas ce temps.

Une approche bien meilleure, bien que plus difficile, consisterait en une table de fermeture.
Cela aurait l'avantage supplémentaire que cela fonctionne également sur MySQL.
Une fois que vous avez implémenté la fonctionnalité de fermeture une fois, vous la travaillerez à des emplacements supplémentaires en un rien de temps.

Dilemme
la source
3
+1 sur le fait de porter à mon attention les tables de fermeture (du moins la terminologie connaissait déjà le concept). Voici un bon article à ce sujet pour d'autres personnes qui pourraient être intéressées. coderwall.com/p/lixing/closure-tables-for-browsing-trees-in-sql
Outfast Source