примечание: этот вопрос был обновлен, чтобы отразить, что в настоящее время мы используем MySQL, поэтому я хотел бы увидеть, насколько проще будет, если мы перейдем на базу данных, поддерживающую CTE.
У меня есть таблица с самоссылкой с первичным ключом id
и внешним ключом 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 | |
+------------+--------------+------+-----+---------+----------------+
Учитывая name
, как я могу запросить родителя верхнего уровня?
Учитывая name
, как я могу запросить все, что id
связано с записью name = 'foo'
?
контекст: я не являюсь dba, но планирую попросить dba реализовать этот тип иерархической структуры и хотел бы протестировать некоторые запросы. Мотивация для этого описана Kattge et al 2011 .
Вот пример отношений между идентификаторами в таблице:
-- -----------------------------------------------------
-- 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
Дэвид Лебауэр
источник
источник
Ответы:
Вы определенно должны написать это через язык хранимых процедур MySQL
Вот
GetParentIDByID
хранимая функция, вызываемая для получения ParentID с указанным идентификатором для поискаВот
GetAncestry
хранимая функция, вызываемая для получения списка идентификаторов ParentID, начиная с первого поколения, по всей иерархии с указанным идентификатором:Вот что-то для генерации данных примера:
Вот что он генерирует:
Вот что генерируют функции для каждого значения:
МОРАЛЬ ИСТОРИИ: рекурсивный поиск данных должен быть написан в MySQL
ОБНОВЛЕНИЕ 2011-10-24 17:17 ПО ВОСТОЧНОМУ ВРЕМЕНИ
Вот обратная сторона GetAncestry. Я называю это GetFamilyTree.
Вот алгоритм:
Я полагаю, что в моих классах Data Structures and Algorithms в College это называется чем-то вроде обхода дерева предзаказов / префиксов.
Вот код:
Вот что производит каждый ряд
Этот алгоритм работает чисто при условии отсутствия циклических путей. Если существуют какие-либо циклические пути, вам необходимо добавить столбец «посещенные» в таблицу.
После того, как вы добавили посещенный столбец, вот алгоритм, блокирующий циклические отношения:
ОБНОВЛЕНИЕ 2011-10-24 17:37 ПО ВОСТОЧНОМУ ВРЕМЕНИ
Я создал новую таблицу под названием наблюдения и заполнил ваши данные выборки. Я изменил хранимые процедуры, чтобы использовать наблюдения вместо pctable. Вот ваш вывод:
ОБНОВЛЕНИЕ 2011-10-24 18:22 ПО ВОСТОЧНОМУ ВРЕМЕНИ
Я изменил код для GetAncestry. Там
WHILE ch > 1
должно было бытьWHILE ch > 0
Попробуй это сейчас !!!
источник
Получение всех родителей указанного узла:
Чтобы получить корневой узел, вы можете, например,
ORDER BY level
взять первый рядПолучение всех потомков указанного узла:
(обратите внимание, что в рекурсивной части оператора условие перестановки для объединения)
Насколько мне известно, следующие СУБД поддерживают рекурсивные CTE:
редактировать
Исходя из ваших примеров данных, следующее извлечет все поддеревья из таблицы, включая полный путь для каждого узла в качестве дополнительного столбца:
Выход будет такой:
источник
Функция GetFamilyTree в ответе Роландо не работает, если заданный идентификатор превышает 4 целых числа, потому что функция FORMAT MySQL добавляет запятые для тысяч разделителей. Я изменил хранимую функцию GetFamilyTree для работы с большими целочисленными идентификаторами, как показано ниже:
front_id определен внутри цикла if else.
источник