2009-06-10 13 views
2

Ich versuche, eine Abfrage so zu erstellen, dass einige Spalte aus einer vorherigen übereinstimmenden Zeile aufgebaut sind. Zum Beispiel mit folgenden Daten:Oracle SQL Query (Analytics?)

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 

Ich möchte die folgenden Daten abgerufen.

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 

dh für die Zeile 3, es selbst hat den Wert für L2, L1 für sie auf die letzte Zeile zu gehen hat, die L1-Daten enthält, in diesem Fall die erste Zeile.

Ich habe versucht, die Analyse und die connect-Klausel zu betrachten, aber ich kann mich nicht um eine Lösung kümmern.
Irgendwelche Ideen?

Antwort

5

aktualisieren: Es gibt eine viel einfachere Lösung als meine erste Antwort. Es ist besser lesbar und eleganter, Ich halte es daher hier die erste Stelle setzen wird (Wie so oft, dank 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 

Nach meiner ersten Lösung ist:

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 
+0

Das ist wirklich süß, genau das, was ich suchte, ich vermutete, könnte über Analytics, vielen Dank getan werden. Danke auch an Rax und Calmar für ihre Eingabe. – Patrick

+0

Ja, viel viel besser, danke. Ja, ich hatte geahnt, dass du auch ein Tom Kyte Fan von der Art der ersten Antwort warst. – Patrick

0

Haben Sie nur 3 Ebenen (oder eine feste Anzahl von Ebenen)?

Wenn ja, könnte man so etwas zu verwenden, was sehr ineffizient ist, aber ich glaube, Werke (ich es nicht von diesem Computer ausgeführt werden kann, so dass es ein „blind“ Code ist, dass leichte Veränderungen erfordern):

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) 
+0

Ja, Höhe festgesetzt werden - tatsächlich Aufgabe verwendet Level 3 bis 9. Danke für diese Idee, ich hatte es nicht berücksichtigt. Ich hoffe immer noch, dass es einen besseren Weg gibt, ohne die Self-Joins zu arbeiten. – Patrick

0

Um die connect-Klausel zu verwenden, benötigen Sie tatsächlich einige Link-Zeilen. Es sollte also eine Spalte geben, die eine Verknüpfung zur vorherigen Zeile mit dem gewünschten Wert hat. Bei diesen Feldern ist es schwierig, eine Auswahl zu treffen, da für jede Zeile 2 Unterauswahlen benötigt werden, um die anderen Ebenen zu überprüfen.

Ich würde pl/SQL-Verfahren verwenden, wenn es geeignet ist.

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; 
/