ORDEM Hierárquica da Árvore Pai-Filho


21

Eu tenho que seguir os dados no SQL Server 2008 R2. SQLFiddle

Esquema:

CREATE TABLE [dbo]. [ICFilters] (
   [ICFilterID] [int] IDENTIDADE (1,1) NÃO NULL,
   [ParentID] [int] NÃO NULL PADRÃO 0,
   [FilterDesc] [varchar] (50) NÃO NULL,
   [Ativo] [tinyint] NÃO NULL PADRÃO 1,
 CONSTRAINT [PK_ICFilters] CHAVE PRIMÁRIA CLUSTERED 
 ([ICFilterID] ASC) COM 
    PAD_INDEX = OFF,
    STATISTICS_NORECOMPUTE = OFF,
    IGNORE_DUP_KEY = OFF,
    ALLOW_ROW_LOCKS = ATIVADO,
    ALLOW_PAGE_LOCKS = ATIVADO
 ) ON [PRIMÁRIO]
) ON [PRIMÁRIO]

INSERT INTO [dbo]. [ICFilters] (ParentID, FilterDesc, Active)
Valores 
(0, 'Tipo de produto', 1),
(1, 'ProdSubType_1', 1),
(1, 'ProdSubType_2', 1),
(1, 'ProdSubType_3', 1),
(1, 'ProdSubType_4', 1),
(2, 'PST_1.1', 1),
(2, 'PST_1.2', 1),
(2, 'PST_1.3', 1),
(2, 'PST_1.4', 1),
(2, 'PST_1.5', 1),
(2, 'PST_1.6', 1),
(2, 'PST_1.7', 0),
(3, 'PST_2.1', 1),
(3, 'PST_2.2', 0),
(3, 'PST_2.3', 1),
(3, 'PST_2.4', 1),
(14, "PST_2.2.1", 1),
(14, "PST_2.2.2", 1),
(14, "PST_2.2.3", 1),
(3, 'PST_2.8', 1)

Tabela:

| ICFILTERID PARENCIADO FILTERDESC | ATIVO
--------------------------------------------------
| 1 | 0 Tipo de produto 1 |
| 2 1 | ProdSubType_1 | 1 |
| 3 1 | ProdSubType_2 | 1 |
| 4 1 | ProdSubType_3 | 1 |
| 5 1 | ProdSubType_4 | 1 |
| 6 2 PST_1.1 1 |
| 7 2 PST_1.2 1 |
| 8 2 PST_1.3 1 |
| 9 2 PST_1.4 1 |
| 10 2 PST_1.5 | 1 |
| 11 2 PST_1.6 1 |
| 12 2 PST_1.7 0
| 13 3 PST_2.1 1 |
| 14 3 PST_2.2 0
| 15 3 PST_2.3 1 |
| 16 3 PST_2.4 1 |
| 17 14 PST_2.2.1 1 |
| 18 14 PST_2.2.2 1 |
| 19 14 PST_2.2.3 1 |
| 20 3 PST_2.8 1 |

Cada linha tem o ID do pai e da raiz parentid = 0. Os FilterDescsão apenas descrições de amostra, então não posso tentar analisá-las para pedidos.

A questão

É possível selecionar todas as linhas de maneira semelhante a uma árvore? Se sim, como? Quando digo "parecido com uma árvore", quero dizer selecionar recursivamente o pai seguido por todos os seus filhos, depois todos os filhos de cada um deles e assim por diante. Primeira travessia da árvore da profundidade.

Meus amigos e eu tentamos, mas ficamos aquém das soluções de trabalho, mas continuaremos tentando. Eu sou bastante novo no sql, então talvez isso possa ser feito facilmente e estou apenas tornando as coisas mais difíceis do que o necessário.

Exemplo de saída (desejada):

| ICFILTERID PARENCIADO FILTERDESC | ATIVO
--------------------------------------------------
| 1 | 0 Tipo de produto 1 |
| 2 1 | ProdSubType_1 | 1 |
| 6 2 PST_1.1 1 |
| 7 2 PST_1.2 1 |
| 8 2 PST_1.3 1 |
| 9 2 PST_1.4 1 |
| 10 2 PST_1.5 | 1 |
| 11 2 PST_1.6 1 |
| 12 2 PST_1.7 0
| 3 1 | ProdSubType_2 | 1 |
| 13 3 PST_2.1 1 |
| 14 3 PST_2.2 0
| 17 14 PST_2.2.1 1 |
| 18 14 PST_2.2.2 1 |
| 19 14 PST_2.2.3 1 |
| 15 3 PST_2.3 1 |
| 16 3 PST_2.4 1 |
| 20 3 PST_2.8 1 |
| 4 1 | ProdSubType_3 | 1 |
| 5 1 | ProdSubType_4 | 1 |

Seria melhor usar CTE
Kin Shah

1
Aqui está um thread que mostra a classificação do resultado desejado sem que os dados da tabela precisem ser carregados em qualquer ordem específica. Ele usa row_number () e particiona por para criar um "caminho" que permite a classificação desejada. ask.sqlservercentral.com/questions/48518/...

Respostas:


25

OK, células cerebrais suficientes estão mortas.

SQL Fiddle

WITH cte AS
(
  SELECT 
    [ICFilterID], 
    [ParentID],
    [FilterDesc],
    [Active],
    CAST(0 AS varbinary(max)) AS Level
  FROM [dbo].[ICFilters]
  WHERE [ParentID] = 0
  UNION ALL
  SELECT 
    i.[ICFilterID], 
    i.[ParentID],
    i.[FilterDesc],
    i.[Active],  
    Level + CAST(i.[ICFilterID] AS varbinary(max)) AS Level
  FROM [dbo].[ICFilters] i
  INNER JOIN cte c
    ON c.[ICFilterID] = i.[ParentID]
)

SELECT 
  [ICFilterID], 
  [ParentID],
  [FilterDesc],
  [Active]
FROM cte
ORDER BY [Level];

2
Isso é exatamente o que eu precisava! Concordo que muitas células cerebrais morreram por causa disso. Eu não estava claro no que queria? Nesse caso, editarei a pergunta para referência futura. Eu estava definitivamente tornando-se mais difícil do que precisava ser ...
Archangel33

1
@ Archangel33 Você fez um bom trabalho ao definir o problema e o que precisa. Além disso, o sqlfiddle realmente ajuda.
Travis

2
+1, mas usando [ICFilterID] [int] IDENTIDADE (1,1) para classificar funcionará apenas se os itens forem inseridos na sequência correta, mas um outro campo para classificação ainda não foi implementado pelo OT
bummi

4
Não acredito que esta seja uma solução 100% correta. Apesar de listar todas as linhas com seu nível correto na hierarquia, não a lista na ordem em que a pergunta pediu. Seria possível listar as linhas na ordem correta conforme a pergunta? É isso que também estou procurando.

1
Isso responde à minha pergunta, pois os dados fornecidos na [FilterDesc]coluna são fictícios e essa ordem é desnecessária / sem importância. Seguindo a lógica da resposta de Travis Gan, tudo o que você deve fazer para obter essa ordem é adicionar outra CASTà Level. por exemplo. Level + CAST( CAST(i.[ICFilterID] AS varbinary(max)) AS LevelTorna-se Level + CAST(i.[FilterDesc] AS varbinary(max)) + CAST(i.[ICFilterID] AS varbinary(max)) AS Level.
Archangel33

1

O acima exposto não parece funcionar corretamente para mim. Imagine uma configuração de 2 tabelas com dados do tipo facebook. A Tabela 1 apresenta PostId + outros campos. PostId é incremento automático e, obviamente, em sua interface, você classificará DESC para ter a última publicação no topo.

Agora, para a tabela de comentários. Tabela 2 Esta tabela CommentId é a chave primária, número automático. No seu GUI, você deseja exibi-lo em ASC, para que ao ler o tópico, faça sentido. (mais antigo (número menor) na parte superior) Outras chaves importantes na tabela 2 são: PostId (FK de volta às postagens) e ParentId (FK de CommentId), em que ParentId será NULL se esse for o comentário "raiz" em uma postagem. Se alguém responder a um comentário, o parentId será preenchido com o commentid.
Espero que vocês entendam. O CTE ficará assim:

WITH  Comments
        AS ( SELECT  CommentId , ParentId, CAST(CommentId AS VARBINARY(MAX)) AS Sortkey, 0 AS Indent
             FROM    dbo.Comments
             WHERE   ParentId IS NULL AND PostId = 105
             UNION ALL
             SELECT  b.CommentId , b.ParentId,  c.Sortkey + CAST(b.CommentId AS varbinary(max))  AS Sortkey, c.Indent + 1 AS Indent
             FROM    dbo.Comments b
             INNER JOIN Comments c ON c.CommentId = b.ParentId
           )
   SELECT   *
   FROM     Comments
   ORDER BY Sortkey

Saída de amostra

1   NULL    0x0000000000000001  0
5   1   0x00000000000000010000000000000001  1
6   5   0x000000000000000100000000000000010000000000000005  2
2   NULL    0x0000000000000002  0

Na publicação F / B 105, houve dois comentários (CommentIds 1 e 2). Alguém respondeu no Comment1 (CommentId 5, ParentId 1) e depois alguém comentou sobre essa resposta, assim como no Comment5 (CommentId 6, ParentId 6).

E viola, a sequência está correta. No post, agora você pode mostrar os comentários na sequência correta. Para recuar as postagens de forma que elas se formem e contornem como no facebook (quanto mais fundo o nível, mais ele deve ter margens da esquerda), também tenho uma coluna chamada Recuo. As raízes são 0 e, em seguida, na união, temos c.Indent + 1 AS Indent No código, agora você pode multiplicar o indent por vamos assumir 32px e mostrar os comentários em uma boa hierarquia e estrutura de tópicos.

Não vejo problema em usar a chave primária de incremento automático CommentId como a força motriz para criar minha SortKey, pois há uma mudança melhor nas datas que você bagunça (data do comentário) do que bagunçar uma chave gerenciada do banco de dados que semeia +1


0
create table pc ( parent varchar(10), child varchar(10) )

insert into pc values('a','b');
insert into pc values('a','c');
insert into pc values('b','e');
insert into pc values('b','f');
insert into pc values('a','d');
Insert into pc values('b','g');
insert into pc values('c','h');
insert into pc values('c','i');
insert into pc values('d','j');
insert into pc values('f','k');
insert into pc values('x','y');
insert into pc values('y','z');
insert into pc values('m','n');

 DECLARE @parent varchar(10) = 'a';
 WITH cte AS
 (
  select null parent, @parent child, 0 as level
   union
  SELECT  a.parent, a.child , 1 as level
    FROM pc a
   WHERE a.parent = @parent
   UNION ALL
  SELECT a.parent, a.child , c.level +    1
  FROM pc a JOIN cte c ON a.parent = c.child
  )
  SELECT distinct parent, child , level
  FROM cte
  order by level, parent

Isso lhe dará todos os descendentes e o nível.
Espero que isto ajude :)

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.