SQL-запрос Oracle (аналитика?)
Вопрос
Я пытаюсь построить запрос таким образом, чтобы некоторый столбец был создан из предыдущей соответствующей строки.Например, со следующими данными:
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
Я хочу, чтобы были получены следующие данные.
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
т. е. для строки 3 он сам имеет значение для L2, для L1 он должен перейти к самой последней строке, которая содержит данные L1, в данном случае к первой строке.
Я пытался посмотреть на analytics и предложение connect, но не могу найти решение.
Есть какие-нибудь идеи?
Решение
Обновить:существует гораздо более простое решение, чем мой первый ответ.Это более читабельно И элегантно, поэтому я помещу его здесь первым (как часто, благодаря Том Кайт):
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
Ниже приведено мое первоначальное решение:
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
Другие советы
У вас есть только 3 уровня (или фиксированное количество уровней)?
Если это так, вы могли бы использовать что-то подобное, что очень неэффективно, но я верю, что работает (я не могу запустить его с этого компьютера, поэтому это "слепой" код, который может потребовать небольших изменений):
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)
Чтобы использовать предложение connect, вам действительно понадобится какая-то ссылка между строками.Таким образом, должен быть какой-то столбец, который содержал бы ссылку на предыдущую строку, имеющую требуемое значение.С этими полями довольно сложно сделать один выбор, так как вам нужно иметь 2 подвыборки для каждой строки, чтобы проверить другие уровни.
Я бы использовал процедуру pl / sql, если она подходит.
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;
/