Окно аналитической функции Oracle, определяемое данными
-
22-09-2019 - |
Вопрос
У меня есть таблица, которая представляет собой построчный дамп данных, считанных из определенного формата текстового файла.Каждая строка может представлять собой "основную" или "детальную" строку, обозначенную через rec_type
код.Я хотел бы написать запрос, который получает "основные" строки рядом с соответствующими подробными строками.Я придумал кое-что, что справляется с этой задачей, но это кажется немного халтурным, и я заинтересован в лучших способах, если таковые имеются.
CREATE TABLE mdtest
(rec_seq NUMBER PRIMARY KEY
,rec_type VARCHAR2(3) NOT NULL
,rec_data VARCHAR2(100) NOT NULL);
INSERT INTO mdtest VALUES (1, '100', 'Bill Jones');
INSERT INTO mdtest VALUES (2, '200', '20080115,100.25');
INSERT INTO mdtest VALUES (3, '100', 'John Smith');
INSERT INTO mdtest VALUES (4, '200', '20090701,80.95');
INSERT INTO mdtest VALUES (5, '200', '20091231,110.35');
Желаемый результат:
SEQ_EMP EMP_NAME SEQ_DATA EMP_DATA
======= ========== ======== ===============
1 Bill Jones 2 20080115,100.25
3 John Smith 4 20090701,80.95
3 John Smith 5 20091231,110.35
Предположения:
- записи обрабатываются в последовательности rec_seq
- первый тип записи - это "
100
" - каждый "
100
" запись содержит 1 или более записей "200
" записи , следующие
Примечание:это для Oracle 9i, однако в этом году мы должны перейти на 11g R1.
Решение
Вот что у меня есть на данный момент:
SELECT seq_emp
,SUBSTR(emp_seq_name,10) emp_name
,seq_data
,emp_data
FROM (SELECT MAX(CASE WHEN rec_type = '100' THEN rec_seq END)
OVER (ORDER BY rec_seq
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW) seq_emp
,MAX(CASE
WHEN rec_type = '100'
THEN TO_CHAR(rec_seq,'fm00000000') || '|' || rec_data
END)
OVER (ORDER BY rec_seq
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW) emp_seq_name
,rec_seq seq_data
,rec_type
,rec_data emp_data
FROM mdtest)
WHERE rec_type = '200'
ORDER BY seq_data;
Как вы можете видеть, я использую аналитическую функцию MAX reporting с окном, начинающимся с верхней части набора и заканчивающимся текущей строкой, чтобы получить соответствующую запись "100" для текущей записи "200";затем во внешнем запросе я отбрасываю ненужные записи "100".
Чтобы получить emp_name, мне пришлось добавить rec_seq к данным, чтобы функция MAX по-прежнему выбирала правильную запись заголовка;затем во внешнем запросе я отключаю rec_seq .
Я играл с другими аналитическими функциями и синтаксисом, включая FIRST_VALUE и синтаксис KEEP, но ни один из них, похоже, не упрощает эту работу;трудность заключается в том, что окно определяется значением rec_type вместо некоторого постоянного смещения.
Другие советы
Стремясь к простоте, считаете ли вы, что стоит загружать каждый тип записи в отдельную таблицу импорта перед обработкой?
create table mdtest100 as select * from mdtest where rec_type = 100;
create table mdtest200 as select * from mdtest where rec_type = 200;
with mdtest_detail as
(
select
(select max(m.rec_seq) from mdtest100 m
where m.rec_seq < r200.rec_seq) master_rec_seq,
r200.*
from
mdtest200 r200
)
select
m.rec_seq seq_emp,
m.rec_data emp_name,
d.rec_seq seq_data,
d.rec_data emp_data
from
mdtest_detail d
inner join mdtest100 m on m.rec_seq = d.master_rec_seq
order by
seq_emp,
seq_data;
SEQ_EMP EMP_NAME SEQ_DATA EMP_DATA
1 Bill Jones 2 20080115,100.25
3 John Smith 4 20090701,80.95
3 John Smith 5 20091231,110.35
Это может оказаться более удобным в обслуживании решением и позволит вам проанализировать и проверить разделенное запятыми поле EMP_DATA отдельно.
Просто мысль - извините, если вы ищете только аналитическое решение.