tabelle autoreferenziali, buone o cattive? [chiuso]


37

Rappresentando le posizioni geografiche all'interno di un'applicazione, la progettazione del modello di dati sottostante suggerisce due opzioni chiare (o forse più?).

Una tabella con una colonna parent_id autoreferenziale uk - london (london parent id = UK id)

o due tabelle, con una relazione da una a molte utilizzando una chiave esterna.

La mia preferenza è per una tabella autoriflettente in quanto consente di estendere facilmente in tutte le sottoregioni necessarie.

In generale, le persone si allontanano dalle tabelle autoreferenziali o sono A-OK?

Risposte:


40

Nulla di sbagliato con le tabelle autoreferenziali.

È il modello di progettazione di database comune per gerarchie annidate in profondità (infinito?).


@NimChimpsky - Come il concetto di ricorsione, questa idea è difficile per alcuni.
Oded,

2
(Almeno) Oracle ha persino uno speciale contratto SQL, la clausola "START WITH - CONNECT BY", per gestire le tabelle autoreferenziali.
user281377

1
@ user281377 - E SQL Server ha introdotto il hierarchyidtipo.
Oded,

usa il letargo in modo che abbia la sua salsa speciale
NimChimpsky,

4
@NimChimpsky: considera anche il "Modello di set nidificato" come alternativa alla colonna "parent_id" - offre le stesse funzionalità, ma prestazioni migliori e query più semplici per estrarre le gerarchie. en.wikipedia.org/wiki/Nested_set_model La serie di libri di Joe Celko "SQL For Smarties" contiene alcuni esempi di SQL relativi agli insiemi nidificati.
Keith Palmer Jr.

7

Necromancing.
La risposta corretta è: dipende da quale motore di database e da quale strumento di gestione.

Facciamo un esempio:
abbiamo una tabella di report
e un report può avere un genitore (menupoint, come categoria)
e quel genitore stesso può avere un genitore (ad es. Centro di profitto)
e così via all'infinito.

L'esempio più semplice di una relazione ricorsiva standard, come con qualsiasi entità / gerarchia autoreferenziale.

La tabella SQL Server risultante è:

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

Ma ottieni un problema:
quando devi eliminare un menupoint con tutti i suoi sottomenuints, NON PUOI impostare delete-cascade, perché Microsoft SQL Server non supporta le eliminazioni in cascata ricorsive (d'altra parte, PostGreSQL fa [ma solo se il graph non è ciclico], mentre a MySQL non piace affatto questo tipo di struttura di tabelle, perché non supporta CTE ricorsivi.

Quindi si fa saltare in aria con integrità / funzionalità di cancellazione, rendendo obbligatoria l'implementazione di tale funzionalità nel proprio codice o in una procedura memorizzata (se RDBMS supporta le procedure memorizzate).

Ciò senza dubbio farà saltare in aria qualsiasi tipo di importazione / esportazione dinamica di dati completamente automatica, poiché non è possibile eseguire semplicemente un'istruzione di eliminazione per tutte le tabelle in base a relazioni di chiave esterna (senza autoreferenziazione), né è possibile selezionare semplicemente * e crea un inserto per ogni riga in un ordine arbitrario.

Ad esempio, quando si crea uno script INSERT utilizzando SSMS, SSMS non otterrà la chiave esterna e quindi creerà effettivamente istruzioni insert che inseriranno voci con dipendenze, prima che inserisca il padre della dipendenza, che fallirà con un errore , poiché è presente la chiave esterna.

Tuttavia, su adeguati sistemi di gestione del database (come PostgreSQL), con strumenti adeguati, questo non dovrebbe essere un problema. Basta capire che solo perché paghi molto per il tuo RDBMS (ti sto guardando, Microsoft; Oracle =?) E / o la sua cintura degli attrezzi, ciò non significa che sia programmato correttamente. E nemmeno OpenSource (ad esempio MySQL) ti rende immune a queste meravigliose minuzie.

Il diavolo è nei dettagli, come dice il vecchio detto.

Ora, non che non potresti aggirare questi problemi, ma non lo consiglierei davvero, se il tuo sistema sarà complesso (ad es. Oltre 200 tabelle).
Inoltre, in un normale contesto commerciale (come interpretato da Dilbert), non ti verrà dato quel tempo.

Un approccio molto migliore, sebbene più difficile, sarebbe un tavolo di chiusura.
Ciò avrebbe l'ulteriore vantaggio che funziona anche su MySQL.
Una volta implementata la funzionalità di chiusura una volta, la farai funzionare in posti aggiuntivi in ​​pochissimo tempo.


3
+1 su come portare alla mia attenzione i tavoli di chiusura (almeno la terminologia, conoscevo già il concetto). Ecco un buon articolo su di esso per gli altri che potrebbero essere interessati. coderwall.com/p/lixing/closure-tables-for-browsing-trees-in-sql
Fonte Outfast

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.