0

我有一个记录表,其中包含如下所示的数据:

ID 味精 日期
---------------------------------------------
1 测试 2010-01-01 09:00:00
2 工作开始时间 2010-01-01 09:03:00
3 做点什么 2010-01-01 09:03:10
4 做点别的事情 2010-01-01 09:03:12
5 做点什么 2010-01-01 09:04:19
6 工作结束 2010-01-01 09:06:30
7 工作开始 2010-01-01 09:18:03
8 做点什么 2010-01-01 09:18:17
9 做其他事情 2010-01-01 09:19:48
10 工作结束 2010-01-01 09:20:27

它包含(除其他外)应用程序编写的消息。我有兴趣为所有此类“工作开始”和“工作结束”对生成“工作开始”记录和“工作结束”记录之间写入的所有内容的报告。理想情况下,报告应如下所示:

BATCH_NUM ID 消息日期
-------------------------------------------------- --------         
1 3 做点什么 2010-01-01 09:03:10
1 4 做点别的事情 2010-01-01 09:03:12
1 5 做点什么 2010-01-01 09:04:19

2 8 做点什么 2010-01-01 09:18:17
2 9 做其他事情 2010-01-01 09:19:48

(添加批次之间的换行符以使其更易于阅读)

输出报告省略了“Job Start”和“Job End”消息,以及“TEst”消息(存在于一对“Job Start”和“Job End”之外。

我什至不确定从哪里开始编写这种查询,或者为此使用 PL/SQL 是否更好。哎呀,我什至不确定我正在尝试做的事情是否有合适的技术术语。;)

(oracle版本为10g)

4

3 回答 3

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
于 2010-03-03T20:24:29.130 回答
0

以下示例使用与 Adam Musch 的答案大致相同的想法,但在连接到日志记录语句的单个选择中获取日志记录集的开始和结束,而不是使用标量子查询。

我认为 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;
于 2010-03-05T17:23:37.533 回答
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    
于 2016-03-09T14:24:56.567 回答