【问题标题】:Finding runs of a particular value查找特定值的运行
【发布时间】:2009-12-28 21:50:08
【问题描述】:

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

【问题讨论】:

  • 这是一个设计不佳的数据库的完美示例。如果重新设计架构不是您的选择,为了获得一些帮助,您必须解释状态代码之间的关系 - 您的问题不是很清楚。
  • @Miky D,你能解释一下为什么它设计得不好而不是说它是?
  • 结果表中最后一行的规则是什么?那个有一个状态 3 的开始和结束日期时间,而前三行的开始日期时间对应于状态列中的状态,但结束数据时间对应于下一个间隔的状态。
  • 架构可以重新设计,但是以什么方式?该表的主要用途是作为一个进程的记分牌,用于标记它在哪些位置工作的时间。当每个处理步骤对该小时的数据进行时,状态每小时都会发生变化。
  • @Roland 哎呀,应该是 12/11/09 12:00AM。让我解决这个问题。

标签: sql oracle datetime analytics


【解决方案1】:

这是一个 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)

【讨论】:

    【解决方案2】:

    好的,我很抱歉不知道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???
    

    【讨论】:

    • 如果有人同时了解 Sybase 和 Oracle 语法,请随时编辑这篇文章以对其进行 Oraclify... 这是非常通用的事务 SQL,因此应该很容易(SELECT INTO 和 dateadd()是我唯一的嫌疑人)
    • Oracle 没有 DATEADD 函数 - 需要进行一些转换。在此处查看 Oracle 函数:techonthenet.com/oracle/functions/index.php
    【解决方案3】:

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

    基本思路:

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

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

    【讨论】:

    • 我在 PHP 代码中为报告做过类似的事情,但没有尝试将其作为存储过程。我希望直接用 SQL 做起来很简单,但我错过了一些明显的东西 :)
    • 我投了反对票,因为这个解决方案无法扩展。预先进行排序非常昂贵,数据库本质上必须扫描整个表并对所有行进行排序,然后才能进行任何处理行的工作。
    • 看数据本身,如果在(位置,小时)上定义索引,排序应该不会太难
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-09
    • 2013-10-18
    • 1970-01-01
    • 1970-01-01
    • 2012-09-26
    • 1970-01-01
    相关资源
    最近更新 更多