在Oracle中提取唯一时间片

时间:2009-11-20 04:22:54

标签: oracle10g

我使用的是Oracle 10g,我有一个表,可以在给定的一天存储一个人的数据快照。对于核心数据发生任何变化(存储在其他地方)的任何人,每晚都会向表格添加新行。这允许使用日期来编写查询,以找出过去一天中某人“看起来”的样子。即使只有人的一个方面发生了变化,也会向表中添加一个新行 - 这意味着许多列在切片之间具有重复值,因为并非每个快照中的每个细节都发生了更改。

以下是数据样本:

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 Hierarchical Query(使用CONNECT BY [PRIOR])来获得我想要的东西,但我还没弄清楚如何编写它。也许你可以帮忙。

感谢您的时间和考虑。

3 个答案:

答案 0 :(得分: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 希望所有用户都能点击,即使他们没有更改,所以查询的第一个版本是正确的解决方案。

答案 1 :(得分: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)

答案 2 :(得分: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)