2009-11-20 20 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)