Oracle SQL Query (Analytics?)
Domanda
Sto cercando di costruire una query in modo tale che alcune colonne sono costruiti da una riga corrispondente precedente. Ad esempio, con i seguenti dati:
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
Voglio i seguenti dati recuperati.
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
Per esempio per riga 3, si ha il valore di L2, L1 deve andare alla più recente riga contenente dati L1, in questo caso la prima riga.
ho provato guardando analisi e la clausola di connessione, ma non riesco a ottenere la mia testa intorno ad una soluzione.
Tutte le idee?
Soluzione
Aggiorna : c'è una soluzione molto più semplice di quanto la mia prima risposta. E 'più leggibile e più elegante, io quindi metterlo qui prima (come spesso, grazie 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
In seguito è la mia soluzione iniziale:
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
Altri suggerimenti
Hai solo 3 livelli (o un numero fisso di livelli)?
Se è così, si potrebbe usare qualcosa di simile, che è molto inefficiente, ma credo che le opere (non riesco a farlo funzionare da questo computer, quindi è un codice "cieca" che può richiedere lievi modifiche):
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)
Per usare la clausola connect si sarebbe in realtà bisogno di un po 'di legame betwean righe. Quindi ci dovrebbe essere una colonna che avrebbe collegamento alla riga precedente che ha il valore desiderato. Con questi campi si è un pò difficile fare una selezionare come è necessario disporre di 2 subselect per ogni riga di controllare gli altri livelli.
Vorrei usare pl / sql procedura se è adatto.
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;
/