2010-03-03 3 views
0

У меня есть таблица протоколирования, которая содержит данные, выглядит следующим образом:Oracle - группировка между парами записей

 
ID  MSG    DATE 
--------------------------------------------- 
1  TEst    2010-01-01 09:00:00 
2  Job Start   2010-01-01 09:03:00 
3  Do something  2010-01-01 09:03:10 
4  Do something else 2010-01-01 09:03:12 
5  Do something  2010-01-01 09:04:19 
6  Job End   2010-01-01 09:06:30 
7  Job Start   2010-01-01 09:18:03 
8  Do something  2010-01-01 09:18:17 
9  Do other thing  2010-01-01 09:19:48 
10  Job End   2010-01-01 09:20:27 

Он содержит (помимо всего прочего) messags написанных приложений. Я заинтересован в подготовке отчета обо всем, что написано между записью «Job Start» и записью «Job End», для всех таких пар «Job Start» и «Job End». В идеале отчет будет выглядеть следующим образом:

 
BATCH_NUM ID  MSG    DATE 
---------------------------------------------------------   
1   3  Do something  2010-01-01 09:03:10 
1   4  Do something else 2010-01-01 09:03:12 
1   5  Do something  2010-01-01 09:04:19 

2   8  Do something  2010-01-01 09:18:17 
2   9  Do other thing  2010-01-01 09:19:48 

(разрыв строки между партиями добавлены, чтобы сделать его легче читать)

В докладе выход опускает «Работа Пуск и„Работа Конец сообщения“, а также сообщение «TEst» (которое существует вне пары «Job Start» и «Job End».

Я даже не знаю, с чего начать писать такой запрос, или если лучше пойти с PL/SQL для этого. Черт, я даже не уверен, есть ли подходящий технический термин для того, что я пытаюсь сделать;;)

(версия оракула 10g)

+2

Возможно, было бы проще назначить уникальную ссылку на задание (например, из последовательности) и сохранить ее в таблице ведения журнала. Затем вы можете использовать группировку более эффективно. –

+0

@ar: Да, это было бы идеально, и это сделало бы эту проблему не проблемой. К сожалению, мне не хватает контроля над всеми системами для их реализации. – FrustratedWithFormsDesigner

ответ

1

Я уверен, что есть более аналитический способ сделать это, но я обманываю с помощью скалярных подзапросов для создания окон.

SQL> select * from logging_table; 

     ID MSG       LOG_DT 
---------- ------------------------------ ------------------- 
     1 TEst       2010-01-01 09:00:00 
     2 Job Start      2010-01-01 09:03:00 
     3 Do something     2010-01-01 09:03:10 
     4 Do something else    2010-01-01 09:03:12 
     5 Do something     2010-01-01 09:04:19 
     6 Job End      2010-01-01 09:06:30 
     7 Job Start      2010-01-01 09:18:03 
     8 Do something     2010-01-01 09:18:17 
     9 Do other thing     2010-01-01 09:19:48 
     10 Job End      2010-01-01 09:20:27 

SQL> l 
    1  select dense_rank() over (order by job_start_id) as batch, 
     -- ^-- this part gets the batch 
    2    job_step_id, msg, log_dt 
    3    -- nested select to filter out rows outside of the boundaries 
    4  from (select * 
    5    from (select id as job_step_id, msg, log_dt, 
    6        -- scalar subquery to get start of "window" 
    7        (select max(id) 
    8        from logging_table 
    9        where msg = 'Job Start' 
10         and id < log.id) as job_start_id, 
11        -- scalar subquery to get end of "window" 
12        (select min(id) 
13        from logging_table 
14        where msg = 'Job End' 
15         and id > log.id) as job_end_id 
16      from logging_table log 
17      -- filter out the "window" rows themselves 
18      where msg not in ('Job Start', 'Job End') 
19     ) 
20    -- the filtering out of "unbounded" records 
21    where job_start_id is not null 
22     and job_end_id is not null 
23   ) 
24*  order by job_step_id 
SQL>/

BATCH JOB_STEP_ID MSG       LOG_DT 

 1   3 Do something     2010-01-01 09:03:10 
    1   4 Do something else    2010-01-01 09:03:12 
    1   5 Do something     2010-01-01 09:04:19 
    2   8 Do something     2010-01-01 09:18:17 
    2   9 Do other thing     2010-01-01 09:19:48 
+0

Да, я думаю, что это то, что мне нужно, я попробовал, и похоже, что он делает то, что я хотел. 'dense_rank() over (order by job_start_id)' похоже, является ключом. Мне нужно будет прочитать эту функцию. – FrustratedWithFormsDesigner

+0

Раздел аналитики http://download.oracle.com/docs/cd/B19306_01/server.102/b14223/analysis.htm#i1007779 является смехотворно плотным. Для того, чтобы понять это, почти нужно много проблем с историями (например, на StackOverflow!). У AskTom.oracle.com есть множество примеров, использующих аналитику. –

0

Следующий пример использует много ту же идею, ответ Адама Musch, но получает начало Множества Logging и заканчиваться в один выбор, который присоединился к заявлению лесозаготовительной, а не с помощью скалярные подзапросы.

Я не думаю, что DENSE_RANK() может использоваться больше напрямую, потому что для разделения не существует столбца.

Кроме того, любое из этих решений предполагает, что никогда не будет перекрывать множество журналов. Если второй набор начинается до того, как первый один заканчивается, это совершенно новая проблема ...

WITH logging_sets AS 
    (SELECT DENSE_RANK() OVER (ORDER BY start_date) AS set_rank, start_date, end_date 
     FROM (SELECT CASE msg 
         WHEN 'Job End' 
          THEN NULL 
         ELSE LEAD (log_dt, 1, NULL) OVER (ORDER BY log_dt) 
        END AS end_date, log_dt AS start_date, msg 
       FROM logging_table lt 
       WHERE msg IN ('Job Start', 'Job End')) 
     WHERE msg = 'Job Start') 
SELECT ls.set_rank, lt.ID, lt.msg, lt.log_dt 
    FROM logging_table lt, logging_sets ls 
WHERE lt.log_dt > ls.start_date AND lt.log_dt < ls.end_date 
    AND msg NOT IN ('Job Start', 'Job End') 
ORDER BY ls.set_rank, lt.log_dt; 
0

Вот способ сделать то же трюк без использования оконных функций, на всякий случай.

with logging_table as (
    select 1 id, 'job start' msg from dual union 
    select 2, 'do somenthing in batch 1' from dual union 
    select 3, 'do somenthing else in batch 1' from dual union 
    select 4, 'job end' from dual union 
    select 5, 'job start' from dual union 
    select 6, 'do somenthing in batch 2' from dual union 
    select 7, 'do somenthing else in batch 2' from dual union  
    select 8, 'job end' from dual 
), 
jobs as (
    select lt_start.id id_start, 
      (select min(id) 
      from logging_table lt_end 
      where lt_end.id > lt_start.id 
      and msg = 'job end') id_end, 
      rownum as batch_no 
    from logging_table lt_start 
    where msg = 'job start' 
) 
select * 
from logging_table join jobs 
     on id > id_start and id < id_end 
order by batch_no, id  
Смежные вопросы