ORDEN jerárquica del árbol padre-hijo

21

Tengo que seguir los datos en SQL Server 2008 R2. SQLFiddle

Esquema:

CREATE TABLE [dbo]. [ICFilters] (
   [ICFilterID] [int] IDENTITY (1,1) NOT NULL,
   [ParentID] [int] NO NULL DEFAULT 0,
   [FilterDesc] [varchar] (50) NO NULL,
   [Activo] [tinyint] NO NULO POR DEFECTO 1,
 RESTRICCIÓN [PK_ICFilters] CLAVE PRIMARIA CLUSIFICADA 
 ([ICFilterID] ASC) CON 
    PAD_INDEX = OFF,
    STATISTICS_NORECOMPUTE = OFF,
    IGNORE_DUP_KEY = OFF,
    ALLOW_ROW_LOCKS = ON,
    ALLOW_PAGE_LOCKS = ON
 ) ENCENDIDO [PRIMARIO]
) ENCENDIDO [PRIMARIO]

INSERT INTO [dbo]. [ICFilters] (ParentID, FilterDesc, Active)
Valores 
(0, 'Tipo de producto', 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)

Mesa:

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

Cada fila tiene la ID de su padre y la raíz parentid = 0. Los FilterDescs son solo descripciones de muestra, así que no puedo tratar de analizarlos para ordenarlos.

La pregunta

¿Es posible seleccionar todas las filas en forma de árbol? ¿Si es así, cómo? Cuando digo 'en forma de árbol', me refiero a seleccionar recursivamente el padre seguido de todos sus hijos, luego todos los hijos de cada uno de ellos y así sucesivamente. Una profundidad del primer recorrido del árbol.

Mis amigos y yo lo hemos intentado, pero nos hemos quedado cortos de soluciones de trabajo, pero seguiremos intentándolo. Soy bastante nuevo en SQL, así que tal vez esto se pueda hacer fácilmente y solo estoy haciendo las cosas más difíciles de lo necesario.

Ejemplo de salida (deseada):

El | ICFILTERID | PARENTID | FILTERDESC | ACTIVO |
--------------------------------------------------
El | 1 | 0 | Tipo de producto | 1 |
El | 2 | 1 | ProdSubType_1 | 1 |
El | 6 | 2 | PST_1.1 | 1 |
El | 7 | 2 | PST_1.2 | 1 |
El | 8 | 2 | PST_1.3 | 1 |
El | 9 | 2 | PST_1.4 | 1 |
El | 10 | 2 | PST_1.5 | 1 |
El | 11 | 2 | PST_1.6 | 1 |
El | 12 | 2 | PST_1.7 | 0 |
El | 3 | 1 | ProdSubType_2 | 1 |
El | 13 3 | PST_2.1 | 1 |
El | 14 3 | PST_2.2 | 0 |
El | 17 | 14 PST_2.2.1 | 1 |
El | 18 14 PST_2.2.2 | 1 |
El | 19 14 PST_2.2.3 | 1 |
El | 15 | 3 | PST_2.3 | 1 |
El | 16 3 | PST_2.4 | 1 |
El | 20 | 3 | PST_2.8 | 1 |
El | 4 | 1 | ProdSubType_3 | 1 |
El | 5 | 1 | ProdSubType_4 | 1 |
Arcángel33
fuente
Sería mejor usar CTE
Kin Shah
1
Aquí hay un hilo que muestra la clasificación de resultados deseada sin tener que cargar los datos de la tabla en ningún orden específico. Utiliza row_number () y particionar para crear una "ruta" que permite la ordenación deseada. ask.sqlservercentral.com/questions/48518/…

Respuestas:

25

Bien, suficientes células cerebrales están muertas.

Violín de SQL

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];
Travis
fuente
2
¡Esto es exactamente lo que necesitaba! Estoy de acuerdo en que muchas células cerebrales han muerto por esto. ¿No estaba claro en lo que quería? Si es así, editaré la pregunta para referencia futura. Definitivamente lo estaba haciendo más difícil de lo necesario ...
Archangel33
1
@ Archangel33 Hiciste un buen trabajo presentando el problema y lo que necesitas. Además, sqlfiddle realmente ayuda.
Travis
2
+1 pero usando [ICFilterID] [int] IDENTITY (1,1) para ordenar solo funcionará si los elementos se insertan en la secuencia correcta, pero OT
bummi
44
No creo que esta sea una solución 100% correcta. Si bien enumera todas las filas con su nivel correcto en la jerarquía, no la enumera en el orden en que la pregunta les pidió que estuvieran. ¿Sería posible enumerar las filas en el orden correcto según la pregunta? Eso es lo que estoy buscando también.
1
Esto responde a mi pregunta ya que los datos proporcionados en la [FilterDesc]columna son ficticios y ese orden es innecesario / sin importancia. Siguiendo la lógica en la respuesta de @Travis Gan, todo lo que se debe hacer para obtener este pedido es agregar otro CASTal Level. p.ej. Level + CAST( CAST(i.[ICFilterID] AS varbinary(max)) AS LevelSe convierte Level + CAST(i.[FilterDesc] AS varbinary(max)) + CAST(i.[ICFilterID] AS varbinary(max)) AS Level.
Archangel33
1

Lo anterior no parece funcionar correctamente para mí. Imagine una configuración de 2 tablas con datos de tipo Facebook. La Tabla 1 tiene PostId + otros campos. PostId es de incremento automático y, obviamente, en su interfaz, ordenará DESC para tener la última publicación en la parte superior.

Ahora para la tabla de comentarios. Tabla 2 Esta tabla CommentId es la clave principal, el número automático. En su interfaz gráfica de usuario, desea mostrarlo ASC, para que cuando lea el hilo, tenga sentido. (el más antiguo (número más pequeño) en la parte superior) Otras claves importantes en la tabla 2 son: PostId (FK a las publicaciones) y ParentId (FK a CommentId) donde ParentId será NULL si este es el comentario "raíz" en una publicación. Si alguien RESPONDE en un comentario, el parentId se completará con el commentid.
Espero que ustedes se enteren. El CTE se verá así:

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

Salida de muestra

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

En la publicación F / B 105, hubo dos comentarios (CommentIds 1 y 2) Alguien respondió el Comment1 (CommentId 5, ParentId 1), y luego alguien más comentó esa respuesta, así que Comment5 (CommentId 6, ParentId 6)

Y viola, la secuencia es correcta, debajo de la publicación, ahora puede mostrar los comentarios en la secuencia correcta. Para sangrar las publicaciones de modo que se formen y delineen como en Facebook (cuanto más profundo es el nivel, más debe ser marginado desde la izquierda), también tengo una columna llamada Sangría. Las raíces son 0 y luego en la unión, tenemos c.Indent + 1 AS Indent En el código, ahora puede multiplicar la sangría con supongamos 32px y mostrar los comentarios en una buena jerarquía y esquema.

No veo ningún problema al usar la clave primaria de incremento automático CommentId como la fuerza impulsora para construir mi SortKey, ya que hay un mejor cambio en las fechas de desorden (comentario) que la desorganización de una clave administrada de base de datos que se inicia con +1

Guss Davey
fuente
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

Esto te dará todos los descendientes y el nivel.
Espero que esto ayude :)

Wohoooo
fuente