Pregunta

Estoy tratando de crear una consulta de tal manera que alguna columna se construye a partir de una fila coincidente anterior. Por ejemplo, con los siguientes datos:

CREATE TABLE TEST (SEQ NUMBER, LVL NUMBER, DESCR VARCHAR2(10));
INSERT INTO TEST VALUES (1, 1, 'ONE');
INSERT INTO TEST VALUES (2, 2, 'TWO1');
INSERT INTO TEST VALUES (3, 2, 'TWO2');
INSERT INTO TEST VALUES (4, 3, 'THREE1');
INSERT INTO TEST VALUES (5, 2, 'TWO3');
INSERT INTO TEST VALUES (6, 3, 'THREE2');
COMMIT

Quiero los siguientes datos recuperados.

SEQ L1  L2   L3
1   ONE NULL NULL
2   ONE TWO1 NULL
3   ONE TWO2 NULL
4   ONE TWO2 THREE1
5   ONE TWO3 THREE1
5   ONE TWO3 THREE2

es decir, para la fila 3, que en sí tiene el valor de L2, para L1 tiene que ir a la más reciente fila que contiene datos L1, en este caso la primera fila.

He intentado mirar la analítica y la cláusula de conexión, pero no puedo conseguir mi cabeza alrededor de una solución.
¿Alguna idea?

¿Fue útil?

Solución

Actualizar : hay una solución mucho más simple que mi primera respuesta. Es más fácil de leer y más elegante, por lo tanto, voy a poner aquí en primer lugar (como a menudo, gracias a Tom Kyte ):

SQL> SELECT seq,
  2         last_value(CASE
  3                       WHEN lvl = 1 THEN
  4                        descr
  5                    END IGNORE NULLS) over(ORDER BY seq) L1,
  6         last_value(CASE
  7                       WHEN lvl = 2 THEN
  8                        descr
  9                    END IGNORE NULLS) over(ORDER BY seq) L2,
 10         last_value(CASE
 11                       WHEN lvl = 3 THEN
 12                        descr
 13                    END IGNORE NULLS) over(ORDER BY seq) L3
 14    FROM TEST;

       SEQ L1         L2         L3
---------- ---------- ---------- ----------
         1 ONE                   
         2 ONE        TWO1       
         3 ONE        TWO2       
         4 ONE        TWO2       THREE1
         5 ONE        TWO3       THREE1
         6 ONE        TWO3       THREE2

Lo que sigue es mi solución inicial:

SQL> SELECT seq,
  2         MAX(L1) over(PARTITION BY grp1) L1,
  3         MAX(L2) over(PARTITION BY grp2) L2,
  4         MAX(L3) over(PARTITION BY grp3) L3
  5    FROM (SELECT seq,
  6                 L1, MAX(grp1) over(ORDER BY seq) grp1,
  7                 L2, MAX(grp2) over(ORDER BY seq) grp2,
  8                 L3, MAX(grp3) over(ORDER BY seq) grp3
  9             FROM (SELECT seq,
 10                          CASE WHEN lvl = 1 THEN descr END L1,
 11                          CASE WHEN lvl = 1 AND descr IS NOT NULL THEN ROWNUM END grp1,
 12                          CASE WHEN lvl = 2 THEN descr END L2,
 13                          CASE WHEN lvl = 2 AND descr IS NOT NULL THEN ROWNUM END grp2,
 14                          CASE WHEN lvl = 3 THEN descr END L3,
 15                          CASE WHEN lvl = 3 AND descr IS NOT NULL THEN ROWNUM END grp3
 16                     FROM test))
 17   ORDER BY seq;

       SEQ L1         L2         L3
---------- ---------- ---------- ----------
         1 ONE                   
         2 ONE        TWO1       
         3 ONE        TWO2       
         4 ONE        TWO2       THREE1
         5 ONE        TWO3       THREE1
         6 ONE        TWO3       THREE2

Otros consejos

¿Tiene sólo 3 niveles (o un número fijo de niveles)?

Si es así, usted podría utilizar algo por el estilo, que es muy ineficiente, pero creo que las obras (no puedo ejecutarlo desde este equipo por lo que es un código "ciegos" que pueden requerir una ligera alteración):

SELECT COUNTER.SEQ AS SEQ, A.DESCR AS L1, B.DESCR AS L2, C.DESCR AS L3
FROM TABLE AS COUNTER, TABLE AS A, TABLE AS B, TABLE AS C
WHERE
A.SEQ = 
    (SELECT MAX(D.SEQ) FROM TABLE AS D 
     WHERE D.LVL = 1 AND D.SEQ <= COUNTER.SEQ) AND 
B.SEQ = 
    (SELECT MAX(D.SEQ) FROM TABLE AS D 
     WHERE D.LVL = 2 AND D.SEQ <= COUNTER.SEQ) AND
C.SEQ = 
    (SELECT MAX(D.SEQ) FROM TABLE AS D 
     WHERE D.LVL = 3 AND D.SEQ <= COUNTER.SEQ)

Para utilizar la cláusula de conexión usted realmente necesita algún tipo de relación betwean filas. Así que debe haber alguna columna que tiene un enlace a la fila anterior que tiene el valor deseado. Con esos campos es un poco difícil de hacer uno elige como es necesario tener 2 subselects para cada fila para comprobar los otros niveles.

Me gustaría utilizar el procedimiento PL / SQL si es adecuado.

declare
    cursor c_cur is
    select * from test order by seq asc;

lvl1 test.descr%type := null;
lvl2 test.descr%type := null;
lvl3 test.descr%type := null;

begin

for rec in c_cur loop
    if rec.lvl = 1 then
        lvl1 := rec.descr;
    elsif rec.lvl = 2 then
        lvl2 := rec.descr;
    elsif rec.lvl = 3 then
        lvl3 := rec.descr;
    end if;
    dbms_output.put_line(rec.seq||','||nvl(lvl1, 'null')||','||nvl(lvl2, 'null')||','||nvl(lvl3, 'null'));  
end loop;

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