sql >> Base de Datos >  >> RDS >> Mysql

Consulta para obtener registros principales con registros secundarios, seguidos de los siguientes registros principales e secundarios en mysql

La solución que propongo aquí utiliza el concepto de camino materializado. El siguiente es un ejemplo de rutas materializadas usando sus datos de muestra. Espero que te ayude a entender el concepto de camino 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 nodo N tiene un camino materializado, este camino te indica el camino a seguir desde el nodo raíz hasta el nodo N . Se puede compilar concatenando los ID de nodo. Por ejemplo, para llegar al nodo 5 comenzando desde su nodo raíz, visita el nodo 1 , nodo 3 y nodo 5 , por lo que el nodo 5 la ruta materializada es 1.3.5

Coincidentemente, el orden que buscas lo puedes lograr ordenando por el camino materializado.

En el ejemplo anterior, las rutas materializadas se construyen concatenando cadenas, pero prefiero la concatenación binaria por varias razones.

Para construir las rutas materializadas, necesita el siguiente 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 |
+----+--------------------------+----------+----------------------------+

El CTE recursivo anterior comienza con los nodos raíz. Calcular la ruta materializada para un nodo raíz es trivialmente sencillo, es la ID del nodo en sí. En la siguiente iteración, el CTE une los nodos raíz con sus nodos secundarios. La ruta materializada para un nodo hijo CN es la concatenación de la ruta materializada de su nodo padre PN y el id del nodo CN . Las iteraciones subsiguientes avanzan un nivel hacia abajo en el árbol hasta llegar a los nodos hoja.