Selbstreferenzierende Tabellen, gut oder schlecht? [geschlossen]

37

Das Design des zugrunde liegenden Datenmodells, das geografische Standorte innerhalb einer Anwendung darstellt, schlägt zwei klare Optionen vor (oder vielleicht mehr?).

Eine Tabelle mit einer selbstreferenzierenden parent_id-Spalte uk - london (london parent id = UK id)

oder zwei Tabellen mit einer Eins-zu-Viele-Beziehung unter Verwendung eines Fremdschlüssels.

Ich bevorzuge eine selbstreferenzierende Tabelle, da sie problemlos die Erweiterung auf beliebig viele Unterregionen ermöglicht.

Im Allgemeinen weichen die Leute von selbstreferenzierenden Tabellen ab, oder sind sie A-OK?

NimChimpsky
quelle

Antworten:

40

An selbstreferenzierenden Tabellen ist nichts auszusetzen.

Dies ist das übliche Datenbankentwurfsmuster für tief (unendlich?) Verschachtelte Hierarchien.

Oded
quelle
@NimChimpsky - Wie das Konzept der Rekursion ist diese Idee für einige schwierig.
Oded
2
(Zumindest) Oracle hat sogar eine spezielle SQL-Struktur, die "START WITH - CONNECT BY" -Klausel, um sich mit selbst referenzierenden Tabellen zu befassen.
user281377
1
@ user281377 - Und SQL Server hat den hierarchyidTyp eingeführt.
Oded
Verwenden Sie den Winterschlaf, damit er seine eigene Spezialsauce hat
NimChimpsky 30.11.12
4
@NimChimpsky - Betrachten Sie das "Nested Set Model" als Alternative zu dieser "parent_id" -Spalte - es bietet die gleiche Funktionalität, aber eine bessere Leistung und einfachere Abfragen zum Extrahieren der Hierarchien. en.wikipedia.org/wiki/Nested_set_model Joe Celkos Buchreihe "SQL For Smarties" enthält einige großartige SQL-Beispiele für verschachtelte Mengen.
Keith Palmer Jr.
7

Nekromanzierung.
Die richtige Antwort lautet: Es kommt darauf an, welche Datenbank-Engine und welches Management-Tool verwendet wird.

Lassen Sie uns ein Beispiel machen:
Wir haben eine Berichtstabelle,
und ein Bericht kann ein übergeordnetes Element haben (Menüpunkt, wie Kategorie),
und dieses übergeordnete Element kann selbst ein übergeordnetes Element haben (z. B. Profit Center)
und so weiter.

Das einfachste Beispiel für eine standardmäßige rekursive Beziehung, wie bei jeder selbstreferenzierenden Entität / Hierarchie.

Die resultierende SQL-Server-Tabelle lautet:

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

Aber Sie bekommen ein Problem:
Wenn Sie einen Menupunkt mit all seinen Unterpunkten löschen müssen, können Sie delete-cascade NICHT setzen, weil Microsoft SQL-Server rekursive kaskadierte Löschvorgänge nicht unterstützt (PostGreSQL hingegen [, aber nur wenn graph is not cyclic], während MySQL diese Art von Tabellenstruktur überhaupt nicht mag, weil es keine rekursiven CTEs unterstützt).

Sie können damit also die Löschintegrität / -funktionalität in die Luft jagen, sodass die Implementierung dieser Funktionalität in Ihrem eigenen Code oder in einer gespeicherten Prozedur (sofern Ihr RDBMS gespeicherte Prozeduren unterstützt) obligatorisch ist.

Dies wird zweifellos jede Art von vollautomatischem dynamischem Datenimport / -export in die Luft jagen, da Sie weder einfach eine Löschanweisung für alle Tabellen gemäß (nicht selbstreferenzierenden) Fremdschlüsselbeziehungen ausführen noch eine einfache Auswahl vornehmen können * und erstellen Sie eine Einfügung für jede Zeile in einer beliebigen Reihenfolge.

Wenn Sie beispielsweise ein INSERT-Skript mit SSMS erstellen, erhält SSMS den Fremdschlüssel nicht und erstellt daher tatsächlich Insert-Anweisungen, mit denen Einträge mit Abhängigkeiten eingefügt werden, bevor das übergeordnete Element der Abhängigkeit eingefügt wird, was mit einem Fehler fehlschlägt , weil der Fremdschlüssel vorhanden ist.

Auf ordnungsgemäßen Datenbankverwaltungssystemen (wie PostgreSQL) sollte dies jedoch mit geeigneten Tools kein Problem darstellen. Nur verstehen, dass nur, weil Sie viel für Ihr RDBMS (ich sehe Sie, Microsoft; Oracle =?) Und / oder dessen Tool-Belt bezahlen, es nicht bedeutet, dass es richtig programmiert ist. Und OpenSource (zB MySQL) macht Sie auch nicht immun gegen solch wunderbare Kleinigkeiten.

Der Teufel steckt im Detail, wie das alte Sprichwort sagt.

Nun, nicht, dass Sie solche Probleme nicht umgehen könnten, aber ich würde es wirklich nicht empfehlen, wenn Ihr System komplex sein wird (z. B. mehr als 200 Tabellen).
Außerdem wird Ihnen in einem üblichen kommerziellen Umfeld (wie von Dilbert dargestellt) diese Zeit nicht gewährt.

Ein viel besserer, wenn auch schwierigerer Ansatz wäre ein Abschlusstisch.
Das hätte den zusätzlichen Bonus, dass es auch auf MySQL funktioniert.
Sobald Sie die Closure-Funktionalität einmal implementiert haben, können Sie sie in kürzester Zeit an weiteren Stellen einsetzen.

Dilemma
quelle
3
+1 über das Hervorheben von Abschlusstabellen (zumindest die Terminologie kannte das Konzept bereits). Hier ist ein guter Artikel darüber für andere, die interessiert sein könnten. coderwall.com/p/lixing/closure-tables-for-browsing-trees-in-sql
Outfast Source