Pregunta

Tengo una tabla similar a esta:

CREATE TABLE example (
  id integer primary key,
  name char(200),
  parentid integer,
  value integer);

Puedo usar el campo parentid para organizar los datos en una estructura de árbol.

Ahora aquí está la parte que no puedo resolver.Dado un parentid, ¿es posible escribir una declaración SQL para sumar todos los campos de valor bajo ese parentid y recurrir a la rama del árbol?

ACTUALIZAR: Estoy usando posgreSQL, por lo que las sofisticadas funciones de MS-SQL no están disponibles para mí.En cualquier caso, me gustaría que esto se tratara como una pregunta SQL genérica.

Por cierto, ¡estoy muy impresionado de tener 6 respuestas dentro de los 15 minutos posteriores a hacer la pregunta!¡Vaya al desbordamiento de pila!

¿Fue útil?

Solución

Hay algunas formas de hacer lo que necesita en PostgreSQL.

Algo como esto:

create or replace function example_subtree (integer)
returns setof example as
'declare results record;
         child record;
 begin
  select into results * from example where parent_id = $1;
  if found then
    return next results;
    for child in select id from example
                  where parent_id = $1
      loop
        for temp in select * from example_subtree(child.id)
        loop
          return next temp;
        end loop;
      end loop;
  end if;
  return null;
end;' language 'plpgsql';

select sum(value) as value_sum
  from example_subtree(1234);

Otros consejos

A continuación se muestra un script de ejemplo que utiliza una expresión de tabla común:

with recursive sumthis(id, val) as (
    select id, value
    from example
    where id = :selectedid
    union all
    select C.id, C.value
    from sumthis P
    inner join example C on P.id = C.parentid
)
select sum(val) from sumthis

El script anterior crea una tabla 'virtual' llamada sumthis que tiene columnas id y val.Se define como el resultado de dos selecciones fusionadas con union all.

Primero select obtiene la raíz (where id = :selectedid).

Segundo select sigue a los hijos de los resultados anteriores de forma iterativa hasta que no hay nada que devolver.

El resultado final se puede procesar como una tabla normal.En este caso se suma la columna val.

Desde la versión 8.4, PostgreSQL tiene soporte de consulta recursiva para expresiones de tabla comunes usando el estándar SQL WITH sintaxis.

Si desea una solución portátil que funcione en cualquier ANSI SQL-92 RDBMS, deberá agregar una nueva columna a su tabla.

Joe Celko es el autor original del Conjuntos anidados enfoque para almacenar jerarquías en SQL.Puedes buscar en Google Jerarquía de "conjuntos anidados" para comprender más sobre los antecedentes.

O simplemente puedes cambiar el nombre de parentid a izquierdista y agrega un ID derecho.

Aquí está mi intento de resumir los conjuntos anidados, que lamentablemente se quedarán cortos porque no soy Joe Celko:SQL es un lenguaje basado en conjuntos y el modelo de adyacencia (almacenamiento de ID principal) NO es una representación de una jerarquía basada en conjuntos.Por lo tanto, no existe un método basado exclusivamente en conjuntos para consultar un esquema de adyacencia.

Sin embargo, la mayoría de las principales plataformas han introducido extensiones en los últimos años para abordar este problema preciso.Entonces, si alguien responde con una solución específica de Postgres, úsela por todos los medios.

Una forma estándar de realizar una consulta recursiva en SQL son recursivos CTE. PostgreSQL los apoya desde 8.4.

En versiones anteriores, puedes escribir una función recursiva de retorno de conjuntos:

CREATE FUNCTION fn_hierarchy (parent INT)
RETURNS SETOF example
AS
$$
        SELECT  example
        FROM    example
        WHERE   id = $1
        UNION ALL
        SELECT  fn_hierarchy(id)
        FROM    example
        WHERE   parentid = $1
$$
LANGUAGE 'sql';

SELECT  *
FROM    fn_hierarchy(1)

Vea este artículo:

Si utiliza SQL Server 2005, existe una forma realmente interesante de hacerlo utilizando expresiones de tabla comunes.

Elimina todo el trabajo duro de crear una tabla temporal y básicamente le permite hacerlo todo con solo CON y UNION.

Aquí hay un buen tutorial:

http://searchwindevelopment.techtarget.com/tip/0,289483,sid8_gci1278207,00.html

usar una expresión de tabla común.

Es posible que desee indicar que se trata de SQL Server 2005 o superior únicamente. Dale Ragan

aquí hay un artículo en recursividad por parte de SqlTeam sin expresiones de tabla comunes.

El siguiente código se compila y se prueba correctamente.

create or replace function subtree (bigint)
returns setof example as $$
declare
    results record;
    entry   record;
    recs    record;
begin
    select into results * from example where parent = $1;
    if found then
        for entry in select child from example where parent = $1 and child  parent loop
            for recs in select * from subtree(entry.child) loop
                return next recs;
            end loop;
        end loop;
    end if;
    return next results;
end;
$$ language 'plpgsql';

La condición "niño <> padre" es necesaria en mi caso porque los nodos apuntan a sí mismos.

Divertirse :)

Oracle tiene "COMENZAR CON" y "CONECTAR POR"

select 
    lpad(' ',2*(level-1)) || to_char(child) s

from 
    test_connect_by 

start with parent is null
connect by prior child = parent;

http://www.adp-gmbh.ch/ora/sql/connect_by.html

Solo como breve comentario, aunque la pregunta ha sido respondida muy bien, cabe señalar que si tratamos esto como:

pregunta SQL genérica

entonces la implementación de SQL es bastante sencilla, ya que SQL'99 permite la recursividad lineal en la especificación (aunque creo que ningún RDBMS implementa el estándar completamente) a través del WITH RECURSIVE declaración.Entonces, desde una perspectiva teórica, podemos hacer esto ahora mismo.

Ninguno de los ejemplos funcionó bien para mí, así que lo arreglé así:

declare
    results record;
    entry   record;
    recs    record;
begin
    for results in select * from project where pid = $1 loop
        return next results;
        for recs in select * from project_subtree(results.id) loop
            return next recs;
        end loop;
    end loop;
    return;
end;

¿Es este servidor SQL?¿No podrías escribir un procedimiento almacenado TSQL que recorra y una los resultados?

También me interesa saber si existe una forma de hacer esto únicamente mediante SQL.Por lo que recuerdo de mi clase de bases de datos geográficas, debería haberlas.

Creo que es más fácil en SQL 2008 con JerarquíaID

Si necesita almacenar gráficos arbitrarios, no solo jerarquías, puede dejar Postgres a un lado y probar una base de datos de gráficos como AllegroGraph:

Todo en la base de datos del gráfico se almacena como un triple (nodo de origen, borde, nodo de destino) y le brinda soporte de primera clase para manipular la estructura del gráfico y consultarla utilizando un lenguaje similar a SQL.

No se integra bien con algo como Hibernate o Django ORM, pero si se toma en serio las estructuras de gráficos (no solo las jerarquías como las que le brinda el modelo Nested Set), compruébelo.

También creo que Oracle finalmente ha agregado soporte para gráficos reales en sus últimos productos, pero me sorprende que haya tardado tanto, muchos problemas podrían beneficiarse de este modelo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top