Dada uma tabela hierárquica como esta:
CREATE TABLE [dbo].[btree]
(
id INT PRIMARY KEY
, parent_id INT REFERENCES [dbo].[btree] ([id])
, name NVARCHAR(20)
);
Eu gostaria de obter toda a estrutura da árvore.
Por exemplo, usando estes dados:
INSERT INTO [btree] VALUES (1, null, '1 Root');
INSERT INTO [btree] VALUES (2, 1, '1.1 Group');
INSERT INTO [btree] VALUES (3, 1, '1.2 Group');
INSERT INTO [btree] VALUES (4, 2, '1.1.1 Group');
INSERT INTO [btree] VALUES (5, 2, '1.1.2 Group');
INSERT INTO [btree] VALUES (6, 3, '1.2.1 Group');
INSERT INTO [btree] VALUES (7, 3, '1.2.2 Group');
INSERT INTO [btree] VALUES (8, 4, '1.1.1.1 Items');
INSERT INTO [btree] VALUES (9, 4, '1.1.1.2 Items');
INSERT INTO [btree] VALUES (10, 5, '1.1.2.1 Items');
INSERT INTO [btree] VALUES (11, 5, '1.1.1.2 Items');
INSERT INTO [btree] VALUES (12, 6, '1.2.1.1 Items');
INSERT INTO [btree] VALUES (13, 6, '1.2.1.2 Items');
INSERT INTO [btree] VALUES (14, 7, '1.2.2.1 Items');
Eu gostaria de obter:
+----+-----------+---------------------+
| id | parent_id | description |
+----+-----------+---------------------+
| 1 | NULL | 1 Root |
| 2 | 1 | 1.1 Group |
| 4 | 2 | 1.1.1 Group |
| 8 | 4 | 1.1.1.1 Items |
| 9 | 4 | 1.1.1.2 Items |
| 5 | 2 | 1.1.2 Group |
| 10 | 5 | 1.1.2.1 Items |
| 11 | 5 | 1.1.2.2 Items |
| 3 | 1 | 1.2 Group |
| 6 | 3 | 1.2.1 Group |
| 12 | 6 | 1.2.1.1 Items |
| 13 | 6 | 1.2.1.2 Items |
| 7 | 3 | 1.2.2 Group |
| 14 | 7 | 1.2.2.1 Items |
+----+-----------+---------------------+
Estou buscando registros usando uma consulta recursiva como esta:
;WITH tree AS
(
SELECT c1.id, c1.parent_id, c1.name, [level] = 1
FROM dbo.[btree] c1
WHERE c1.parent_id IS NULL
UNION ALL
SELECT c2.id, c2.parent_id, c2.name, [level] = tree.[level] + 1
FROM dbo.[btree] c2 INNER JOIN tree ON tree.id = c2.parent_id
)
SELECT tree.level, tree.id, parent_id, REPLICATE(' ', tree.level - 1) + tree.name AS description
FROM tree
OPTION (MAXRECURSION 0)
;
E este é o resultado atual:
+----+-----------+---------------------+
| id | parent_id | description |
| 1 | NULL | 1 Root |
| 2 | 1 | 1.1 Group |
| 3 | 1 | 1.2 Group |
| 6 | 3 | 1.2.1 Group |
| 7 | 3 | 1.2.2 Group |
| 14 | 7 | 1.2.2.1 Items |
| 12 | 6 | 1.2.1.1 Items |
| 13 | 6 | 1.2.1.2 Items |
| 4 | 2 | 1.1.1 Group |
| 5 | 2 | 1.1.2 Group |
| 10 | 5 | 1.1.2.1 Items |
| 11 | 5 | 1.1.1.2 Items |
| 8 | 4 | 1.1.1.1 Items |
| 9 | 4 | 1.1.1.2 Items |
+----+-----------+---------------------+
Não consigo descobrir como ordená-lo por níveis.
Existe uma maneira de definir uma classificação para cada subnível?
Eu montei um Rextester
t-sql
sql-server-2014
hierarchy
McNets
fonte
fonte
c2.id
substituída por um número de linha e preenchida à esquerda, para que todas as partes tenham o mesmo comprimento. Caso contrário, não funcionará para todos os dados. Basta substituir 2 com 55 dos dados e das mudanças de ordemPath
com uma pequena correção, para adicionar preenchimento.Trapaça, só um pouquinho;) Olha ma, sem recursão!
Testado em rextester.com
Claro que o acima exposto é bastante limitado. Funciona apenas sob as premissas:
name
coluna armazenou (na primeira parte) o "caminho" real.CAST .. AS int
é necessário apenas se as peças forem números.Explicação: O código funciona usando a função
PARSENAME()
que tem como principal objetivo dividir o nome de um objeto em suas 4 partes:Observe que a ordem é inversa. Como exemplo,
PARSENAME('dbo.btree', 2)
nos dará'dbo'
como resultado. Com 3, obteremos NULL (é por isso queREVERSE()
é usado duas vezes no código. Caso contrário, obteríamos os nulos no início. Eles'1.2'
seriam analisadosnull, null, 1, 2
quando desejássemos1, 2, null, null
. )Conclusão: depois de tudo isso, devo acrescentar que a resposta de Bob Campbel é o caminho a seguir, pois é mais geral e produz (na coluna "caminho" no resultado) a hierarquia de caminhos, que pode ser usada para o
ORDER BY
.Outras opções que você pode considerar - se o tamanho da tabela aumentar e a solução recursiva ficar lenta - é realmente armazenar o caminho em uma coluna separada (em um formato adequado para pedidos, por exemplo, com preenchimento) ou usar o fornecido
HierarchyID
tipo exatamente para esse caso de uso, dados hierárquicos.fonte
name
não pode ser usado neste caso. Levarei a noite toda para decifrá-lo, eu poderia ter alguma explicação?name
armazenar um caminho (com texto), como'order173.palletA27.box9'.bag3A
, você ainda pode usar o código (basta remover os lançamentos para int). De qualquer forma, a consulta de BenCambell é o caminho a percorrer em geral.