我在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中做到这一点的想法?谢谢!
答案 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)
有没有想过存储过程?我认为这将是最具可读性的解决方案。
基本理念:
您需要测试它是否也是最快的方式。根据记录的数量,这可能根本不是问题。