查找特定值的运行

时间:2009-12-28 21:50:09

标签: sql oracle datetime analytics

我在Oracle 10中有一个表,其定义如下:

LOCATION   HOUR              STATUS
--------------------------------------
10         12/10/09 5:00PM      1
10         12/10/09 6:00PM      1
10         12/10/09 7:00PM      2
10         12/10/09 8:00PM      1
10         12/10/09 9:00PM      3
10         12/10/09 10:00PM     3
10         12/10/09 11:00PM     3

此表继续针对不同位置和少量状态值。每行为一个位置覆盖一小时。在该小时的过程中从特定位置收集数据,并以块的形式处理。有时数据可用,有时则不可用,并且信息以状态编码。我试图找到特定状态的运行,以便我可以将上面的表转换为:

LOCATION   STATUS     START               END
-----------------------------------------------------------
10         1          12/10/09 5:00PM     12/10/09 7:00PM
10         2          12/10/09 7:00PM     12/10/09 8:00PM  
10         1          12/10/09 8:00PM     12/10/09 9:00PM
10         3          12/10/09 9:00PM     12/11/09 12:00AM 

基本上将表格缩减为定义特定状态的每个延伸的行。我尝试了各种技巧,例如使用超前/滞后来确定开始和结束的位置等等,但所有这些技巧都遇到了失败。到目前为止唯一有效的技巧是以编程方式逐个通过值,这很慢。有没有直接在Oracle中做到这一点的想法?谢谢!

3 个答案:

答案 0 :(得分:2)

这是一个ANSI SQL解决方案:

select      t1.location
,           t1.status
,           min(t1.hour)                                      AS "start" -- first of stretch of same status
,           coalesce(t2.hour, max(t1.hour) + INTERVAL '1' HOUR) AS "end"
from        t_intervals t1             -- base table, this is what we are condensing
left join   t_intervals t2             -- finding the first datetime after a stretch of t1
on          t1.location = t2.location  -- demand same location
and         t1.hour     < t2.hour      -- demand t1 before t2
and         t1.status  != t2.status    -- demand different status 
left join   t_intervals t3             -- finding rows not like t1, with hour between t1 and t2
on          t1.location = t3.location
and         t1.status  != t3.status
and         t1.hour     < t3.hour
and         t2.hour     > t3.hour
where       t3.status is null          -- demand that t3 does not exist, in other words, t2 marks a status transition
group by    t1.location                -- condense on location, status.
,           t1.status
,           t2.hour                    -- this pins the status transition
order by    t1.location
,           t1.status
,           min(t1.hour)

答案 1 :(得分:1)

好的,我为不了解Oracle语法而道歉,但我希望下面的Sybase足够清楚 (我将它分成3个查询,创建2个临时表以便于阅读,但你可以重新单元作为子查询。我不知道如何在Oracle中添加/减去1小时,dateadd(hh...)在Sybase中执行/ p>

SELECT * FROM T
INTO   #START_OF_PERIODS
WHERE NOT EXISTS (
    SELECT 1 FROM T_BEFORE
    WHERE T.LOCATION = T_BEFORE.LOCATION
      AND T.STATUS   = T_BEFORE.STATUS
      AND T.HOUR     = dateadd(hh, T_BEFORE.HOUR, 1)
   )

SELECT * FROM T
INTO   #END_OF_PERIODS
WHERE NOT EXISTS (
    SELECT 1 FROM T_AFTER
    WHERE T.LOCATION = T_AFTER.LOCATION
      AND T.STATUS   = T_AFTER.STATUS
      AND T.HOUR     = dateadd(hh, T_AFTER.HOUR, -1)
   )

SELECT T1.LOCATION, T1.STATUS, T1.HOUR AS 'START', MIN(T2.HOUR) AS 'END'
FROM   #START_OF_PERIODS 'T1', #END_OF_PERIODS 'T2'
WHERE  T1.LOCATION = T2.LOCATION
  AND  T1.STATUS   = T2.STATUS
  AND  T1.HOUR    <= T2.HOUR
GROUP BY T1.LOCATION, T1.STATUS, T1.HOUR 
    -- May need to add T2.LOCATION, T2.STATUS to GROUP BY???

答案 2 :(得分:0)

有没有想过存储过程?我认为这将是最具可读性的解决方案。

基本理念:

  1. 运行一个select语句,为一个建筑物提供正确顺序的rown
  2. 逐行迭代结果,并在每次状态更改和到达结果集的末尾时写入新的“运行”记录。
  3. 您需要测试它是否也是最快的方式。根据记录的数量,这可能根本不是问题。