在Oracle SQL中选择周围的行

时间:2009-11-09 13:38:10

标签: sql oracle

我们有一个项目,将数据写入日志记录表。现在,在调查问题时,查询问题行并获取周围的行会很有用,这样我们就可以轻松查看导致问题的原因。日志表有一个时间戳字段,所以我们可以按顺序排序。

基本上我想要SQL的“grep -C”。

举个例子,假设我们有一个reference_id列和一个activity_code列。我发现参考ID = 1234有问题,所以我想找到前面的N个活动。

编辑:一些示例数据

Code        Reference   Time
Allocate    ABC1        9:00
Allocate    ABC2        9:01
Problem     MYREF1      9:02
Allocate    ABC3        9:03
Allocate    ABC4        9:03
Problem2    MYREF1      9:04
Allocate    ABC5        9:09

我给了“MYREF1”作为值得关注的东西,但我希望看到同一时间发生的事情。我想要一个查询,它将获取“MYREF1”引用行,以及一些其他行(可能是周围或前面行中的1或2行)。在我的例子中,如果我想要前面的行(类似于grep -B1),那将是ABC2和ABC4

5 个答案:

答案 0 :(得分:2)

这是一种方法的概念分解。

使用行号注释您订购的日志:

WITH ordered_logs AS (
    SELECT ROWNUM r, log_table.*
    FROM log_table 
    ORDER BY timestamp
)
SELECT * FROM ordered_logs;

查找我们正在寻找的中心信息的行号:

SELECT r r0 FROM ordered_logs
WHERE reference_id = 1234; -- or whatever uniquely identifies your problem

浏览它周围的几行:

SELECT * FROM ordered_logs, sought WHERE r BETWEEN r0 - 5 AND r0 + 5;

把它们全部重新组合在一起:

WITH
  ordered_logs AS (
    SELECT ROWNUM r, log_table.*
    FROM log_table 
    ORDER BY timestamp
  ),
  sought AS (
    SELECT r r0
    FROM ordered_logs
    WHERE reference_id = 1234
  )
SELECT *
FROM ordered_logs, sought
WHERE r BETWEEN r0 - 5 AND r0 + 5;

很多优化都是可能的,但这是我能找到的最简单的描述方式。

答案 1 :(得分:2)

有趣的问题。

您可以使用分析函数为您提供您可能感兴趣的时间范围,然后使用此范围从日志记录表中进行选择。

(没有机会运行这个SQL,但它应该给你这个想法)。

这给出了之前2行的TIME和之后的2行:

select
    l.code,
    l.reference,
    l.time,
    min(l.time) over (
        order by l.time 
        rows between 2 preceding and current row) 
            preceding_time,
    max(l.time) over (
        order by l.time 
        rows beween current row and 2 following) 
            following_time
from
    log_table l;

然后,您可以使用这些“时间框”在驾驶表中选择一个范围。

with timebox as
    (
    select
        l.code,
        l.reference,
        l.time,
        min(l.time) over (
            order by l.time 
            rows between 2 preceding and current row) 
                preceding_time,
        max(l.time) over (
            order by l.time 
            rows beween current row and 2 following) 
                following_time
    from
        log_table l
    )
select
    *
from
    log_table a
where
    exists
        (
        select 1 from
            timebox t
        where
            t.reference = 'MYREF1'
        and a.time between t.preceding_time and t.following_time
        );

那是否接近你所追求的目标?

答案 2 :(得分:0)

如果您只想要最新的:n行为1234:

select timestamp, activity_code
from
( select timestamp, activity_code
  from   log
  where  reference_id=1234
  order by timestamp desc
)
where rownum <= :n;

答案 3 :(得分:0)

create table grep_like (
  id  number,
  dt  date,
  txt varchar2(10)
);


insert into grep_like values(10, sysdate -  9/24/60/60, 'foo');
insert into grep_like values(30, sysdate -  8/24/60/60, 'bar');
insert into grep_like values(39, sysdate -  2/24/60/60, 'baz');
insert into grep_like values(22, sysdate -  5/24/60/60, '***');
insert into grep_like values(87, sysdate -  7/24/60/60, '###');
insert into grep_like values(57, sysdate -  4/24/60/60, '!!!');
insert into grep_like values(32, sysdate +  1/24/60/60, '---');
insert into grep_like values(99, sysdate - 12/24/60/60, '...');
insert into grep_like values(18, sysdate -  1/24/60/60, 'noo');
insert into grep_like values(20, sysdate - 10/24/60/60, 'moo');
insert into grep_like values(81, sysdate -  0/24/60/60, 'huh');


select p.dt, p.txt
from (
  select r.dt, r.txt, r.r,
         max(case when r.id = 57 then r.r else 0 end) over () p
  from (
    select dt, txt, id,
           row_number() over (order by dt)  r
      from grep_like
  ) r
) p
where 
  p.r - p.p between -1 and 1
;

答案 4 :(得分:0)

另一种想法是以这样的方式抓住几分钟的条目:

WITH ts as (
    SELECT timestamp
    FROM log_table
    WHERE reference_id = 1234)
SELECT *
FROM log_table join ts
WHERE timestamp > ts.timestamp - 5 minutes
AND timestamp < ts.timestamp + 5 minutes

当然,'+/- 5分钟'必须根据您的db-system实现。

这可能比获取rownumber并使用它来定义“窗口”要容易一些,但它可能无法满足您的要求。