Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Consulta para obter registros pai com registro filho, seguido pelos próximos registros pai-filho no mysql


A solução que proponho aqui utiliza o conceito de caminho materializado. Veja a seguir um exemplo de caminhos materializados usando seus dados de amostra. Espero que ajude você a entender o conceito de caminho materializado:
+----+--------------------------+----------+------------------+
| ID |           Name           | ParentID | MaterializedPath |
+----+--------------------------+----------+------------------+
|  1 | Parent 1                 |        0 | 1                |
|  2 | Parent 2                 |        0 | 2                |
|  4 | Parent 2 Child 1         |        2 | 2.4              |
|  6 | Parent 2 Child 1 Child 1 |        4 | 2.4.6            |
|  7 | Parent 2 Child 1 Child 2 |        4 | 2.4.7            |
|  3 | Parent 1 Child 1         |        1 | 1.3              |
|  5 | Parent 1 Child 1 Child   |        3 | 1.3.5            |
+----+--------------------------+----------+------------------+

Cada nó N tem um caminho materializado, este caminho informa o caminho para ir do nó raiz até o nó N . Pode ser construído concatenando os id's do nó. Por exemplo, para alcançar o nó 5 a partir de seu nó raiz, você visita o nó 1 , nó 3 , e nó 5 , então o nó 5 o caminho materializado é 1.3.5

Coincidentemente, a ordem que você procura pode ser alcançada ordenando pelo caminho materializado.

No exemplo anterior, os caminhos materializados são strings de concatenação, mas eu prefiro a concatenação binária por vários motivos.

Para construir os caminhos materializados, você precisa do seguinte CTE recursivo:
CREATE TABLE Tree
(
    ID int NOT NULL CONSTRAINT PK_Tree PRIMARY KEY, 
    Name nvarchar(250) NOT NULL,
    ParentID int NOT NULL,
)

INSERT INTO Tree(ID, Name, ParentID) VALUES
(1, 'Parent 1', 0),
(2, 'Parent 2', 0),
(3, 'Parent 1 Child 1', 1),
(4, 'Parent 2 Child 1', 2),
(5, 'Parent 1 Child 1 Child', 3),
(6, 'Parent 2 Child 1 Child 1', 4),
(7, 'Parent 2 Child 1 Child 2', 4)

GO
WITH T AS
(
    SELECT
        N.ID, N.Name, N.ParentID, CAST(N.ID AS varbinary(512)) AS MaterializedPath
    FROM
        Tree N
    WHERE
        N.ParentID = 0

    UNION ALL

    SELECT
        N.ID, N.Name, N.ParentID, CAST( T.MaterializedPath + CAST(N.ID AS binary(4)) AS varbinary(512) ) AS MaterializedPath
    FROM
        Tree N INNER JOIN T
            ON N.ParentID = T.ID

)
SELECT *
FROM T
ORDER BY T.MaterializedPath

Resultado:
+----+--------------------------+----------+----------------------------+
| ID |           Name           | ParentID |      MaterializedPath      |
+----+--------------------------+----------+----------------------------+
|  1 | Parent 1                 |        0 | 0x00000001                 |
|  3 | Parent 1 Child 1         |        1 | 0x0000000100000003         |
|  5 | Parent 1 Child 1 Child   |        3 | 0x000000010000000300000005 |
|  2 | Parent 2                 |        0 | 0x00000002                 |
|  4 | Parent 2 Child 1         |        2 | 0x0000000200000004         |
|  6 | Parent 2 Child 1 Child 1 |        4 | 0x000000020000000400000006 |
|  7 | Parent 2 Child 1 Child 2 |        4 | 0x000000020000000400000007 |
+----+--------------------------+----------+----------------------------+

O CTE recursivo acima começa com os nós raiz. Calcular o caminho materializado para um nó raiz é trivialmente simples, é o ID do próprio nó. Na próxima iteração, o CTE une nós raiz com seus nós filhos. O caminho materializado para um nó filho CN é a concatenação do caminho materializado de seu nó pai PN e o id do nó CN . As iterações subsequentes avançam um nível abaixo na árvore até que os nós folha sejam alcançados.