AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 160924
Accepted
McNets
McNets
Asked: 2017-01-13 13:59:34 +0800 CST2017-01-13 13:59:34 +0800 CST 2017-01-13 13:59:34 +0800 CST

Posso obter uma estrutura de árvore de uma tabela autorreferenciada (hierárquica)?

  • 772

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 esses 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');

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 configurei umRextester

t-sql sql-server-2014
  • 2 2 respostas
  • 23432 Views

2 respostas

  • Voted
  1. Best Answer
    Ben Campbell
    2017-01-13T15:17:12+08:002017-01-13T15:17:12+08:00

    Adicione um campo "caminho" e classifique por semelhante a um caminho de arquivo. Como o ypercube mencionou, a classificação é excessivamente simplista neste exemplo e simplesmente funciona, mas, para simplificar, deixarei como está. Na maioria das vezes, quando uso esse padrão, classifico por nome em vez de ID.

    IF OBJECT_ID('[dbo].[btree]', 'U') IS NOT NULL 
        DROP TABLE [dbo].[btree];
    GO
    
    CREATE TABLE [dbo].[btree]
    (
      id INT PRIMARY KEY
    , parent_id INT REFERENCES [dbo].[btree] ([id])
    , name NVARCHAR(20)
    );
    GO
    
    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.2.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');
    
    ;WITH tree AS
    (
        SELECT c1.id, c1.parent_id, c1.name, [level] = 1, path = cast('root' as varchar(100))
        FROM dbo.[btree] c1
        WHERE c1.parent_id IS NULL
        UNION ALL
        SELECT c2.id, c2.parent_id, c2.name, [level] = tree.[level] + 1, 
               Path = Cast(tree.path+'/'+right('000000000' + cast(c2.id as varchar(10)),10) as varchar(100))
        FROM dbo.[btree] c2 INNER JOIN tree ON tree.id = c2.parent_id
    )
    SELECT tree.path, tree.id, parent_id, REPLICATE('  ', tree.level - 1) + tree.name AS description
    FROM tree
    Order by path
    OPTION (MAXRECURSION 0)
    ;
    

    aqui um rextester

    • 7
  2. ypercubeᵀᴹ
    2017-01-13T15:26:28+08:002017-01-13T15:26:28+08:00

    Trapaça, só um pouquinho ;) Olha ma, sem recursão!

    Testado em rextester.com

    SELECT btree.*        -- , a,b,c,d     -- uncomment to see the parts
    FROM btree 
      OUTER APPLY
        ( SELECT rlc = REVERSE(LEFT(name, CHARINDEX(' ', name)-1))) AS r
      OUTER APPLY
        ( SELECT a = CAST(REVERSE(PARSENAME(r.rlc, 1)) AS int),
                 b = CAST(REVERSE(PARSENAME(r.rlc, 2)) AS int),
                 c = CAST(REVERSE(PARSENAME(r.rlc, 3)) AS int),
                 d = CAST(REVERSE(PARSENAME(r.rlc, 4)) AS int)
        ) AS p 
    ORDER BY a, b, c, d ;
    

    Claro que o acima é bastante limitado. Ele funciona apenas sob as suposições:

    • a namecoluna armazenou (na primeira parte) o "caminho" real.
    • a profundidade da árvore é no máximo 4 (portanto, o caminho tem até 4 partes).
    • o CAST .. AS inté necessário apenas se as partes forem números.

    Explicação: O código funciona usando a função PARSENAME()que tem como objetivo principal dividir um nome de objeto em suas 4 partes:

    Server.Database.Schema.Object
      |        |       |      |
     4th      3rd     2nd    1st
    

    Note que a ordem é inversa. Como exemplo, PARSENAME('dbo.btree', 2)nos dará 'dbo'como resultado. Com 3, obteremos NULL (é por isso que o REVERSE()é usado duas vezes no código. Caso contrário, obteríamos os nulos no início. O '1.2'seria analisado null, null, 1, 2enquanto queremos 1, 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 "path" no resultado) a hierarquia do caminho, que pode ser usada para o arquivo ORDER BY.

    Outras opções que você pode considerar - se o tamanho da tabela aumentar e a solução recursiva se tornar lenta - é armazenar o caminho em uma coluna separada (em um formato bom para ordenação, ou seja, com preenchimento) ou usar o fornecido HierarchyIDtipo que é exatamente para este caso de uso, dados hierárquicos.

    • 4

relate perguntas

  • Como alterar as configurações do gerenciador de configuração do servidor SQL usando o TSQL?

  • Como posso obter uma lista de nomes e tipos de coluna de um conjunto de resultados?

  • MS SQL: Use o valor calculado para calcular outros valores

  • Como posso saber se um banco de dados SQL Server ainda está sendo usado?

  • Implementando uma consulta PIVOT

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve