Hinweis: Diese Frage wurde aktualisiert, um zu berücksichtigen, dass wir derzeit MySQL verwenden. Nachdem dies geschehen ist, würde ich gerne sehen, wie viel einfacher es wäre, auf eine CTE-unterstützende Datenbank umzusteigen.
Ich habe eine selbstreferenzierende Tabelle mit einem Primärschlüssel id
und einem Fremdschlüssel parent_id
.
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| parent_id | int(11) | YES | | NULL | |
| name | varchar(255) | YES | | NULL | |
| notes | text | YES | | NULL | |
+------------+--------------+------+-----+---------+----------------+
Bei einem name
, wie kann ich abfragen , um die Top-Level - Eltern?
name
Wie kann ich bei gegebener a alle id
mit einem Datensatz von verknüpften abfragen name = 'foo'
?
Kontext: Ich bin kein DBA, habe aber vor, einen DBA zu bitten, diese Art von hierarchischer Struktur zu implementieren, und möchte einige Abfragen testen. Die Motivation dazu wird von Kattge et al. 2011 beschrieben .
Hier ist ein Beispiel für die Beziehungen zwischen den IDs in der Tabelle:
-- -----------------------------------------------------
-- Create a new database called 'testdb'
-- -----------------------------------------------------
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';
CREATE SCHEMA IF NOT EXISTS `testdb` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci ;
USE `testdb` ;
-- -----------------------------------------------------
-- Table `testdb`.`observations`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `testdb`.`observations` (
`id` INT NOT NULL ,
`parent_id` INT NULL ,
`name` VARCHAR(45) NULL ,
PRIMARY KEY (`id`) )
ENGINE = InnoDB;
SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
-- -----------------------------------------------------
-- Add Example Data Set
-- -----------------------------------------------------
INSERT INTO observations VALUES (1,3), (2,5), (3,NULL), (4,10),
(5,NULL), (6,1), (7,5), (8,10), (9,10), (10,3);
quelle
Antworten:
Sie müssen dies definitiv über MySQL Stored Procedure Language schreiben
Hier ist eine gespeicherte Funktion, die aufgerufen wird,
GetParentIDByID
um eine ParentID abzurufen, die eine ID hat, nach der gesucht werden sollHier ist eine gespeicherte Funktion, die aufgerufen wird,
GetAncestry
um eine Liste von ParentIDs ab der ersten Generation in der gesamten Hierarchie abzurufen, die eine ID erhalten hat, mit der begonnen wird:Hier ist etwas zum Generieren von Beispieldaten:
Folgendes wird generiert:
Die Funktionen generieren für jeden Wert Folgendes:
MORAL DER GESCHICHTE: Der rekursive Datenabruf muss in MySQL ausgeführt werden
UPDATE 2011-10-24 17:17 EDT
Hier ist die Umkehrung von GetAncestry. Ich nenne es GetFamilyTree.
Hier ist der Algorithmus:
Ich glaube aus meinen Datenstruktur- und Algorithmusklassen in College, dass dies so etwas wie eine Vorbestellungs- / Präfix-Baumdurchquerung genannt wird.
Hier ist der Code:
Hier ist, was jede Zeile erzeugt
Dieser Algorithmus funktioniert einwandfrei, sofern keine zyklischen Pfade vorhanden sind. Wenn es zyklische Pfade gibt, müssten Sie der Tabelle eine besuchte Spalte hinzufügen.
Nachdem Sie die besuchte Spalte hinzugefügt haben, blockiert der folgende Algorithmus zyklische Beziehungen:
UPDATE 2011-10-24 17:37 EDT
Ich habe eine neue Tabelle mit dem Namen "Beobachtungen" erstellt und Ihre Beispieldaten ausgefüllt. Ich habe die gespeicherten Prozeduren geändert, um Beobachtungen anstelle von pctable zu verwenden. Hier ist Ihre Ausgabe:
UPDATE 2011-10-24 18:22 EDT
Ich habe den Code für GetAncestry geändert. Da sollte
WHILE ch > 1
es seinWHILE ch > 0
Probieren Sie es jetzt !!!
quelle
Erste alle Eltern eines bestimmten Knoten:
Um den Wurzelknoten zu erhalten, können Sie zB
ORDER BY level
die erste Reihe nehmenErste alle Kinder eines bestimmten Knoten:
(Beachten Sie die Auslagerungsbedingung für den Join im rekursiven Teil der Anweisung.)
Meines Wissens unterstützen die folgenden DBMS rekursive CTEs:
Bearbeiten
Basierend auf Ihren Beispieldaten werden im Folgenden alle Teilbäume aus der Tabelle einschließlich des vollständigen Pfads für jeden Knoten als zusätzliche Spalte abgerufen:
Die Ausgabe wäre:
quelle
Die GetFamilyTree-Funktion in Rolando Antwort funktioniert nicht, wenn die angegebene ID mehr als 4 Ganzzahlen ist, weil die FORMAT MySQL-Funktion Kommas für tausend Trennzeichen hinzufügt. Ich habe die gespeicherte Funktion GetFamilyTree geändert, um mit den folgenden großen Ganzzahl-IDs zu arbeiten:
front_id innerhalb der if else-Schleife definiert.
quelle