note: esta pergunta foi atualizada para refletir que atualmente estamos usando o MySQL. Assim, eu gostaria de ver como seria mais fácil se mudássemos para um banco de dados compatível com CTE.
Eu tenho uma tabela de auto-referência com uma chave primária id
e uma chave estrangeira 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 | |
+------------+--------------+------+-----+---------+----------------+
Dado a name
, como posso consultar o pai de nível superior?
Dado a name
, como posso consultar todos os id
associados a um registro de name = 'foo'
?
contexto: eu não sou um dba, mas estou planejando pedir a um dba para implementar esse tipo de estrutura hierárquica e gostaria de testar algumas consultas. A motivação para isso é descrita por Kattge et al 2011 .
Aqui está um exemplo dos relacionamentos entre os IDs na tabela:
-- -----------------------------------------------------
-- 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
fonte
fonte
Respostas:
Você definitivamente precisa criar um script via MySQL Stored Procedure Language
Aqui está uma função armazenada chamada
GetParentIDByID
para recuperar um ParentID dado um ID para procurarAqui está uma função armazenada chamada
GetAncestry
para recuperar uma lista de ParentIDs a partir da primeira geração em toda a hierarquia fornecida com um ID para começar:Aqui está algo para gerar dados de amostra:
Aqui está o que ele gera:
Aqui está o que as funções geram para cada valor:
MORAL DA HISTÓRIA: A recuperação recursiva de dados deve estar em script no MySQL
UPDATE 2011-10-24 17:17 EDT
Aqui está o contrário de GetAncestry. Eu chamo isso de GetFamilyTree.
Aqui está o algoritmo:
Acredito que, nas minhas aulas de Estruturas de Dados e Algoritmos, no College, isso é chamado de passagem de árvore de pré-encomenda / prefixo.
Aqui está o código:
Aqui está o que cada linha produz
Esse algoritmo funciona corretamente, desde que não haja caminhos cíclicos. Se existir algum caminho cíclico, você precisará adicionar uma coluna 'visitada' à tabela.
Depois de adicionar a coluna visitada, eis o algoritmo que bloqueia os relacionamentos cíclicos:
ATUALIZAÇÃO 24-10-2011 17:37 EDT
Criei uma nova tabela chamada observações e preencha seus dados de amostra. Alterei os procedimentos armazenados para usar observações em vez de pctable. Aqui está a sua saída:
UPDATE 2011-10-24 18:22 EDT
Mudei o código para GetAncestry. Havia
WHILE ch > 1
isso deveria serWHILE ch > 0
Tente agora !!!
fonte
Obtendo todos os pais de um nó especificado:
Para obter o nó raiz, você pode, por exemplo,
ORDER BY level
e pegar a primeira linhaObtendo todos os filhos de um nó especificado:
(observe a condição trocada para a junção na parte recursiva da instrução)
Que eu saiba, os seguintes DBMS oferecem suporte a CTEs recursivas:
Editar
Com base nos dados de amostra, o seguinte recuperaria todas as subárvores da tabela, incluindo o caminho completo para cada nó como uma coluna adicional:
A saída seria esta:
fonte
A função GetFamilyTree na resposta de Rolando não funciona quando o ID fornecido tem mais de 4 números inteiros, porque a função FORMAT MySQL adiciona vírgulas para milhares de separadores. Modifiquei a função armazenada GetFamilyTree para trabalhar com grandes números inteiros, como abaixo:
front_id definido dentro do loop if else.
fonte