Entwickler-Ecke

Datenbanken - Referentielle Integrität einer Tabelle prüfen


Narses - Mi 26.11.08 19:05
Titel: Referentielle Integrität einer Tabelle prüfen
Moin!

Ich brauch mal wieder (My-)SQL-Nachhilfe... ;)

Ich habe eine Tabelle, die vereinfacht so aufgebaut ist:Dabei enthält parent wiederum ein id einer anderen Zeile, kann also als Zeiger auf den Vorgänger aufgefasst werden. Die ID:1 ist die Wurzel des Baums, immer da und kann nicht geändert werden.

Jetzt kommt´s 8) Die Struktur der Zeilen ist wie ein Ordnerbaum im Dateisystem zu verstehen, es ist also ein Baum. Ich muss nun prüfen, ob es zirkuläre bzw. ungültige Referenzen und/oder verwaiste Objekte gibt. :?

OK, ich könnte jetzt hingehen und die Struktur per einzelner Selects lesen und in PHP/Delphi abbilden, dann die üblichen verdächtigen Algorithmen aus der Graphentheorie anwenden - aber geht das nicht auch "elegant(er)" mit SQL? :nixweiss:

cu
Narses

//EDIT: Tabellenstruktur angepasst


Xentar - Mi 26.11.08 19:26

Weiß nicht, ob das in MySQL auch so ist, da ich selber nur mit Interbase arbeite
Du könntest Next als Foreign Key auf Tabelle.ID deklarieren. Somit MUSS die ID existieren, die du in Next angibst.


SQL-Anweisung
1:
ALTER TABLE TABLE1 ADD CONSTRAINT TABLE1_FK FOREIGN KEY (nextREFERENCES TABLE1(ID) ON UPDATE CASCADE ON DELETE CASCADE;                    

Das "On update" bzw. "on delete" gibt an, was passieren soll, wenn eine ID geändert / gelöscht wird.
Mit Cascade, werden alle Next's, die darauf zeigen, mit angepasst (Achtung: Beim Delete werden diese mit gelöscht!)

Edit: Da fällt mir auf, zirkuläre Verweise findest du damit nicht.. du stellst höchstens sicher, dass die ID, auf die Next zeigt, existiert (sonst gibts nen Fehler beim Anlegen).


Narses - Mi 26.11.08 19:37

Moin!

Du hast mir doch letztes Mal schon geholfen :gruebel: danke für deinen Einsatz! :zustimm:

Nettes Feature, allerdings - wie du schon gesagt hast - damit findet man keine Kreise im Graphen. :(

Ich habe grade gemerkt, dass ich die Tabellen-Struktur nicht korrekt abgebildet habe, es ist keine Nachfolger-, sondern eine Vorgänger-Kette. :idea: Noch ´ne andere Idee? :?

cu
Narses


Christian S. - Mi 26.11.08 19:56

Wenn sicher gestellt ist, dass die ID für jeden neuen Datensatz immer größer wird (Auto-Increment), sollte Dir eine solche Abfrage ungültige Einträge liefern:

SQL-Anweisung
1:
select id from yourtable where parent > id                    


BenBE - Mi 26.11.08 20:47

Du könntest zuerst mal die Referenzielle Integrität der Tabelle prüfen, d.h. dass es keine Ungültigen Verknüpfungen gibt:

Datensätze mit ungültigen Parents (non-existent parent):

SQL-Anweisung
1:
SELECT id FROM tbl WHERE NOT (parentid in (SELECT DISTINCT id from tbl)) AND id <> 1                    


Danach die Anwendung der entsprechenden graphentheoretischen Algorithmen für die Kreis-Erkennung in ne Stored Procedure packen und diese Selecten.

Den Hinweis von Christian kann man hier als goldwerten Tipp nehmen, wenn diese Eigenschaft der Daten bereits gegeben ist. Ansonsten sollte man diese durch einen View entsprechend erzeugen.

Ferner bieten manche DBMS (bekannt von Oracle) sog. Hierarchische Selects an. Diese kann man u.U. dazu missbrauchen, um jegliche Datensätze zu filtern, denen das DBMS keine eindeutige Ebene zuordnen kann. Ist diese Option nicht möglich, kann man dies mit einer\zwei Temporär-Tabellen nachbilden, von denen die erste alle Quell-IDs enthält und die zweite Schritt für Schritt mit all den IDs befüllt wird, die mit den derzeitig in der Tabelle enthaltenen IDs über den Parent erreicht werden können:

SQL-Anweisung
1:
INSERT INTO tmptbl_reachable SELECT id FROM tmptbl_allids WHERE tmptbl_allids.id IN (SELECT id FROM tmptbl_reachable)                    

Solange neue Datensätze hinzugefügt wurden, fortsetzen. Solange Datensatz 1 nicht auf einen weiter unten in der Hierarchie versteckten Datensatz zeigt, ist bei diesem Ansatz die Kreisfreiheit garantiert ;-) (Beweis bleibt dem Fragesteller überlassen).


Narses - Fr 28.11.08 00:52

Moin!

OK, ich merke schon, meine bescheidenen Kenntnisse von SQL sind hier definitiv zu wenig... :(

Also habe ich die Elemente in ein Array gelesen und arbeite im RAM damit, statt das DBMS zu belästigen, geht auch... :? :nixweiss:

Trotzdem danke für die Vorschläge. :)

cu
Narses


Delete - Fr 28.11.08 01:58

Müsste auch so gehen (2 Aliase auf dieselbe Tabelle):

SQL-Anweisung
1:
2:
3:
SELECT A.Id FROM Tabelle A
LEFT JOIN Tabelle B ON B.Id = A.Parent
WHERE B.Id IS NULL