web-dev-qa-db-fra.com

tables auto-référencées, bonnes ou mauvaises?

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 parent_id auto-référencée uk - londres (london parent id = UK id)

ou deux tables, avec une relation un à plusieurs à l'aide d'une clé étrangère.

Ma préférence va à une table auto-référencée car elle permet facilement de s'étendre sur 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 A-OK?

39
NimChimpsky

Rien de mal avec les tables auto-référencées.

Il s'agit du modèle de conception de base de données courant pour les hiérarchies imbriquées en profondeur (infini?).

42
Oded

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

Faisons un exemple:
Nous avons un tableau de rapport,
et un rapport peut avoir un parent (menupoint, comme 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 auto-référencée.

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 avez un problème:
Lorsque vous devez supprimer un menupoint avec tous ses submenupoints, vous NE POUVEZ PAS définir delete-cascade, car Microsoft SQL-Server ne prend pas en charge les suppressions récursives en cascade (d'un autre côté, PostGreSQL le fait [mais seulement si le graphique n'est pas cyclique], tandis que MySQL n'aime pas du tout ce type de structure de table, car il ne prend pas en charge la récursivité CTE).

Donc, vous faites exploser l'intégrité/la fonctionnalité de suppression avec, 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 fera sans aucun doute exploser tout type d'importation/exportation de données dynamiques entièrement automatique, car vous ne pouvez pas simplement exécuter une instruction de suppression pour toutes les tables selon des relations de clé étrangère (non auto-référencées), 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 n'obtiendra pas la clé étrangère, et crée donc effectivement des instructions d'insertion qui inséreront des entrées avec des dépendances, avant d'insérer le parent de la dépendance, qui échouera avec une erreur , car la clé étrangère est en place.

Cependant, sur les systèmes de gestion de base de données appropriés (comme PostgreSQL), avec un outillage approprié, cela ne devrait pas poser de problème. Comprenez que juste parce que vous payez beaucoup pour votre SGBDR (je vous regarde, Microsoft; Oracle =?), Et/ou sa ceinture d'outils, cela ne signifie pas qu'il est programmé correctement. Et OpenSource (par exemple MySQL) ne vous immunise pas non plus contre de si merveilleuses minuties.

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

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

Une bien meilleure approche, bien que plus difficile, serait une table de clôture.
Cela aurait l'avantage supplémentaire qu'il fonctionne également sur MySQL.
Une fois que vous aurez implémenté la fonctionnalité de fermeture une fois, vous la ferez fonctionner dans des endroits supplémentaires en un rien de temps.

7
Quandary

C'est une bonne idée si la relation est réellement hiérarchique et non une relation de réseau (par exemple, une nomenclature est une relation de réseau, pas une relation hiérarchique).

L'interrogation peut être longue. Pour accélérer les choses, vous pouvez utiliser une table de fermeture.

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

1
Neil McGuigan