2009-11-09 132 views
2

我們有一個將數據寫入日誌記錄表的項目。現在,在調查問題時,查詢問題行並獲取周圍的行會很有用,以便我們可以輕鬆查看導致問題的原因。日誌表有一個時間戳字段,所以我們可以按這個順序排列。在Oracle SQL中選擇周圍的行

本質上我想要像「grep -C」的SQL。

舉個例子,假設我們有一個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的周圍或前面的行)。在我的例子,這將是ABC2和ABC4如果我想前行(類似於用grep -B1)

回答

2

有趣的問題。

您可以使用分析函數爲您提供一個您可能感興趣的時間範圍,然後使用此範圍從日誌記錄表中進行選擇。

(沒有機會運行這個SQL,但它應該給你這個想法)。

這爲前2行和後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 
     ); 

這與您接下來的事情接近嗎?

+0

這幾乎就是它。令人沮喪的是,當我使用between子句時,我在數據表上得到了全表掃描,而如果我只是使用= following_time(或類似)子句,我會得到很好的性能。我不知道爲什麼會發生這種情況,但它使這個解決方案不起作用。 – 2009-11-09 17:10:42

+0

您將在某處完全掃描您的log_table到您的log_table。將有一個全表掃描來獲取時間框,但你應該能夠在a.time上進行索引搜索。你看到兩個全表掃描嗎? – 2009-11-09 20:57:04

+0

如果你買不起任何全面掃描,你需要一個索引列,你可以依賴它的「order by」(所以你已經有了'timebox')。如果真實表中的每一行都有一個連續的唯一ID,那麼您可以使用該ID,否則您可能必須回退到使用+/- x分鐘而不是+/- x行。 – 2009-11-09 21:01:16

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; 
+0

這不完全是我想要的 - 我希望所有行(無論參考編號)與我的查詢行及時接近。 – 2009-11-09 13:53:22

+0

「參考號碼」是唯一標識符嗎?看起來不像你的例子。 – 2009-11-09 14:06:12

+0

這不是唯一的否,可以有多行使用相同的參考號碼(想象它是一個訂單號或客戶ID或類似的東西) – 2009-11-09 14:31:02

1

這裏有一個概念上的分解一種方法來做到這一點。

標註您的訂購記錄行號:

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; 

很多優化是可能的,但這是描述它的最簡單的方法,我可以找到。

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 
; 
+0

謝謝 - 這幾乎是我想要的,但它做了全表掃描,它並不是我想要做的事情(這是一張大桌子)。 – 2009-11-09 14:49:47

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-系統。

這可能比獲得一個rownumber並使用它來定義您的「窗口」更容易,但它可能不會滿足您的要求。