Uwaga: pytanie to zostało zaktualizowane, aby odzwierciedlić fakt, że obecnie używamy MySQL. Po zrobieniu tego chciałbym zobaczyć, o ile łatwiej byłoby, gdybyśmy przeszli na bazę danych obsługującą CTE.
Mam tabelę z odnośnikami do siebie z kluczem podstawowym id
i kluczem obcym 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 | |
+------------+--------------+------+-----+---------+----------------+
Biorąc pod uwagę name
, w jaki sposób mogę wysłać zapytanie do rodzica najwyższego poziomu?
Biorąc pod uwagę name
, w jaki sposób mogę zapytać wszystkie id
związane z rekordem name = 'foo'
?
kontekst: Nie jestem dba, ale planuję poprosić dba o implementację tego typu struktury hierarchicznej i chciałbym przetestować niektóre zapytania. Motywację do tego opisali Kattge i in . 2011 .
Oto przykład relacji między identyfikatorami w tabeli:
-- -----------------------------------------------------
-- 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);
mysql
postgresql
hierarchy
David LeBauer
źródło
źródło
Odpowiedzi:
Zdecydowanie musisz to zrobić za pomocą języka procedur przechowywanych MySQL
Oto funkcja
GetParentIDByID
zapisana w celu pobrania identyfikatora nadrzędnego o podanym identyfikatorze do wyszukaniaOto funkcja przechowywana wywoływana
GetAncestry
do pobierania listy ParentIDs, począwszy od pierwszej generacji, od całej hierarchii, której identyfikator zaczyna się od:Oto coś do wygenerowania przykładowych danych:
Oto, co generuje:
Oto, co funkcje generują dla każdej wartości:
MORAL OF THE STORY: Rekurencyjne odzyskiwanie danych musi być skryptowane w MySQL
AKTUALIZACJA 24.10.2011 17:17 EDT
Oto odwrotność GetAncestry. Nazywam to GetFamilyTree.
Oto algorytm:
Uważam, że z moich klas struktur danych i algorytmów w College'u nazywa się to czymś w rodzaju przejścia na drzewo przedpremierowe / przedrostkowe.
Oto kod:
Oto, co produkuje każdy rząd
Algorytm działa poprawnie pod warunkiem, że nie ma ścieżek cyklicznych. Jeśli istnieją jakieś ścieżki cykliczne, musisz dodać kolumnę „odwiedzone” do tabeli.
Po dodaniu odwiedzanej kolumny, oto algorytm blokujący cykliczne relacje:
AKTUALIZACJA 24.10.2011 17:37 EDT
Utworzyłem nową tabelę o nazwie obserwacje i zapełniłem twoje przykładowe dane. Zmieniłem procedury składowane, aby używać obserwacji zamiast pctable. Oto twój wynik:
AKTUALIZACJA 24.10.2011, 18:22 EDT
Zmieniłem kod GetAncestry. Tak
WHILE ch > 1
powinno byćWHILE ch > 0
Spróbuj teraz !!!
źródło
Pobieranie wszystkich rodziców określonego węzła:
Aby uzyskać węzeł główny, możesz np.
ORDER BY level
Wziąć pierwszy wierszPobieranie wszystkich elementów potomnych określonego węzła:
(zwróć uwagę na warunek zamiany dla złączenia w rekurencyjnej części instrukcji)
Według mojej wiedzy następujące DBMS obsługują rekurencyjne CTE:
Edytować
W oparciu o twoje przykładowe dane, poniższe pobrałyby wszystkie poddrzewa z tabeli, w tym pełną ścieżkę dla każdego węzła jako dodatkową kolumnę:
Wynik byłby następujący:
źródło
Funkcja GetFamilyTree w odpowiedzi Rolando nie działa, gdy podany identyfikator ma więcej niż 4 liczby całkowite, ponieważ funkcja FORMAT MySQL dodaje przecinki dla tysięcy separatorów. Zmodyfikowałem zapisaną funkcję GetFamilyTree do pracy z dużymi identyfikatorami całkowitymi, jak poniżej:
front_id zdefiniowany wewnątrz pętli if else.
źródło