2009-11-20 26 views
1

我使用的是Oracle 10g,並且我有一張表,用於存儲某個人某個日期的數據快照。每天晚上,一個外部流程爲任何對其核心數據有任何更改(存儲在別處)的人員添加新行。這允許使用日期來查詢查詢,以查明某個人在過去一天看起來像什麼。即使只有人的一個方面發生了變化,也會向表中添加一個新行 - 這意味着許多列在切片之間具有重複的值,因爲並非每個快照中的每個細節都發生了變化。在Oracle中提取唯一時間片

下面是一個數據樣本:

SliceID PersonID StartDt Detail1 Detail2 Detail3 Detail4 ... 
     1  101 08/20/09  Red Vanilla  N   23 
     2  101 08/31/09 Orange Chocolate  N   23 
     3  101 09/15/09 Yellow Chocolate  Y   24 
     4  101 09/16/09 Green Chocolate  N   24 
     5  102 01/10/09  Blue  Lemon  N   36 
     6  102 01/11/09 Indigo  Lemon  N   36 
     7  102 02/02/09 Violet  Lemon  Y   36 
     8  103 07/07/09  Red  Orange  N   12 
     9  104 01/31/09 Orange  Orange  N   12 
    10  104 10/20/09 Yellow  Orange  N   13 

我需要寫翻出其中一些中肯的比特,而不是整個記錄,改變了時間片記錄的查詢。所以,參考上面的內容,如果我只想知道Detail3已經從其先前的值發生變化的片段,那麼我預計只會得到具有PersonID 101的SliceID 1,3和4以及PersonID的SliceID 5和7的行102和SliceID 8爲PersonID 103和SliceID 9爲PersonID 104.

我想我應該可以使用某種Oracle分層查詢(使用CONNECT BY [PRIOR])來獲得我想要的,但我還沒有想出如何編寫它。也許你可以幫忙。

謝謝你的時間和考慮。

回答

2

這裏我我的取對LAG()溶液,這基本上是相同egorius的,但我顯示我的運作;)

SQL> select * from 
    2 (
    3  select sliceid 
    4    , personid 
    5    , startdt 
    6    , detail3 as new_detail3 
    7    , lag(detail3) over (partition by personid 
    8         order by startdt) prev_detail3 
    9  from some_table 
10 ) 
11 where prev_detail3 is null 
12 or (prev_detail3 != new_detail3) 
13/

    SLICEID PERSONID STARTDT N P 
---------- ---------- --------- - - 
     1  101 20-AUG-09 N 
     3  101 15-SEP-09 Y N 
     4  101 16-SEP-09 N Y 
     5  102 10-JAN-09 N 
     7  102 02-FEB-09 Y N 
     8  103 07-JUL-09 N 
     9  104 31-JAN-09 N 

7 rows selected. 

SQL> 

約這個解決方案的一點是,它在結果中拖拉爲103和104,誰在detail3發生變化時沒有分片記錄。如果這是一個問題,我們可以將附加的過濾與變化只返回行:

SQL> with subq as (
    2  select t.* 
    3    , row_number() over (partition by personid 
    4         order by sliceid) rn 
    5  from 
    6   (
    7    select sliceid 
    8      , personid 
    9      , startdt 
10      , detail3 as new_detail3 
11      , lag(detail3) over (partition by personid 
12           order by startdt) prev_detail3 
13    from some_table 
14   ) t 
15  where t.prev_detail3 is null 
16  or (t.prev_detail3 != t.new_detail3) 
17  ) 
18 select sliceid 
19   , personid 
20   , startdt 
21   , new_detail3 
22   , prev_detail3 
23 from subq sq 
24 where exists (select null from subq x 
25     where x.personid = sq.personid 
26     and x.rn > 1) 
27 order by sliceid 
28/

    SLICEID PERSONID STARTDT N P 
---------- ---------- --------- - - 
     1  101 20-AUG-09 N 
     3  101 15-SEP-09 Y N 
     4  101 16-SEP-09 N Y 
     5  102 10-JAN-09 N 
     7  102 02-FEB-09 Y N 

SQL> 

編輯

由於egorius在評論中指出,做的OP希望所有命中用戶,即使他們沒有改變,所以查詢的第一個版本是正確的解決方案。

+0

慢慢地越來越近:) 雖然daddy6Elbows說他希望SliceID 8爲PersonID 103和SliceID 9爲PersonID 104. – 2009-11-20 09:13:04

+0

謝謝。每個人都有很好的答案,但我必須點頭表示最完整的答案 - 包括例子和額外的評論。但是我給每個人一點意見,因爲他們在技術上都是正確的。 – witttness 2009-11-20 16:00:14

1

我認爲你將有更好的運氣與LAG功能:

SELECT s.sliceid 
    FROM (SELECT t.sliceid, 
       t.personid, 
       t.detail3, 
       LAG(t.detail3) OVER (PARTITION BY t.personid ORDER BY t.startdt) 'prev_val' 
      FROM TABLE t) s 
WHERE s.personid = 101 
    AND (s.prev_val IS NULL OR s.prev_val != s.detail3) 

子查詢分解替代:

WITH slices AS (
    SELECT t.sliceid, 
     t.personid, 
     t.detail3, 
     LAG(t.detail3) OVER (PARTITION BY t.personid ORDER BY t.startdt) 'prev_val' 
    FROM TABLE t) 
SELECT s.sliceid 
    FROM slices s 
WHERE s.personid = 101 
    AND (s.prev_val IS NULL OR s.prev_val != s.detail3) 
1

除了OMG小馬的回答:如果您需要查詢片所有人,你需要partition by

SELECT s.sliceid 
     , s.personid 
    FROM (SELECT t.sliceid, 
       t.personid, 
       t.detail3, 
       LAG(t.detail3) OVER (
        PARTITION BY t.personid ORDER BY t.startdt 
       ) prev_val 
      FROM t) s 
    WHERE (s.prev_val IS NULL OR s.prev_val != s.detail3)