1

我在 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 中做的想法吗?谢谢!

4

3 回答 3

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)
于 2009-12-28T22:25:45.620 回答
1

好的,我为不知道 Oracle 语法而道歉,但我希望下面的 Sybase 足够清楚(我将它分成 3 个查询,创建 2 个临时表以提高可读性,但您可以重新组合为子查询。我不知道如何在 Oracle 中加/减 1 小时,dateadd(hh...)在 Sybase 中可以吗

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???
于 2009-12-28T22:07:05.767 回答
0

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

基本理念:

  1. 运行一个 select 语句,以正确的顺序为您提供一个建筑物的 rown
  2. 逐行遍历结果,并在每次状态更改和到达结果集末尾时写入新的“运行”记录。

您需要测试它是否也是最快的方法。根据记录的数量,这可能根本不是问题。

于 2009-12-28T22:18:23.180 回答