Oracle查询以获取记录在今天之前具有特定状态的天数

时间:2015-07-27 03:33:51

标签: sql oracle

我需要查询以获取上次仪表上线的天数。 例如:

METER  PDATE  STATUS
ABC    1-Jan  off
ABC    2-Jan  on
ABC    3-Jan  on
ABC    4-Jan  on
ABC    5-Jan  off
ABC    6-Jan  off
ABC    7-Jan  on
ABC    8-Jan  on
ABC    9-Jan  off

如果今天是1月8日,则查询将返回:3(1月2日至4日)。

如果今天是1月9日,则查询将返回:2(1月7日至8日)。

我的查询工作正常,但如果应用于具有5百万条记录的真实表,则需要40-50秒。 如果有更快的方法来获取此类数据,请告诉我。

with last_off as
(  
  select meter,pdate lastoff from
  (
      select meter, pdate, 
             row_number() over (partition by meter order by pdate desc) rnum 
      from mytable 
      where status = 'off'
  )
  where rnum=1    
),
last_on as
(
    select meter, laston from
    (
        select a.meter, a.pdate laston, b.lastoff, 
               row_number() over (partition by a.meter order by a.pdate desc) rnum
        from mytable a, last_off b 
        where status = 'on'
          and a.meter=b.meter(+) and a.pdate < b.lastoff
    )
    where rnum=1
),
days_on as
(
    select meter, laston-pdate dayson from
    (
        select a.meter, a.pdate, b.laston, 
               row_number() over (partition by a.meter order by a.pdate desc) rnum
        from mytable a, last_on b 
        where status = 'off'
          and a.meter=b.meter(+) and a.pdate < b.laston
    )
    where rnum=1
)
select meter, dayson
from days_on

3 个答案:

答案 0 :(得分:1)

with t as (
  select meter, pdate, status, 
      case when lag(status)  over (partition by meter order by pdate) 
                < status then 1 end chg1,
      case when lead(status) over (partition by meter order by pdate) 
                < status then 1 end chg2
    from mytable),
d2 as (
  select meter, max(pdate) do2 
    from t where chg2 = 1 and pdate < date '2015-01-09' group by meter),
d1 as (
  select meter, max(pdate) do1 from t join d2 using (meter) 
    where chg1 = 1 and pdate < d2.do2 group by meter)
select meter, do2-do1+1 days_on from d1 join d2 using (meter)

SQLFiddle demo

将包含date '2015-01-09'的行中的值更改为您想要的任何值probably trunc(sysdate)。同时将最后一行更改为:

select meter, count(1) cnt from t join d1 using (meter) join d2 using (meter) 
  where pdate between do1 and do2 group by (meter)

如果你想计算主表中的行而不是简单的减去天数。

答案 1 :(得分:0)

这将获得已经开启的电表列表,以及它们已开启的天数。

(警告:我没有Oracle实例在我编写时尝试此操作)

select maxon.METER,
       (maxon.maxdate - maxoff.maxdate) as dayson
  from
   (select METER,
           Max(PDATE) maxdate
      from MY_TABLE
     where PSTATUS = 'on'
     group by meter) as maxon,
   (select METER,
           Max(PDATE) maxdate
      from MY_TABLE
     where PSTATUS = 'off'
     group by meter) as maxoff
where maxon.meter = maxoff.meter
  and maxon.maxdate > maxoff.maxdate;

你可以结合第二个查询来获取已经关闭的米,或者只是更巧妙地解释减法结果(即,做一个CASE语句,如果结果为负,那么它是关闭,如果是肯定的,那就是) http://www.techonthenet.com/oracle/functions/case.php

答案 2 :(得分:0)

数据设置:

CREATE TABLE my_table
    (METER varchar2(3), PDATE date, STATUS varchar2(3))
;

INSERT ALL 
    INTO my_table (METER, PDATE, STATUS)
         VALUES ('ABC', '01-Jan-2001', 'off')
    INTO my_table (METER, PDATE, STATUS)
         VALUES ('ABC', '02-Jan-2001', 'on')
    INTO my_table (METER, PDATE, STATUS)
         VALUES ('ABC', '03-Jan-2001', 'on')
    INTO my_table (METER, PDATE, STATUS)
         VALUES ('ABC', '04-Jan-2001', 'on')
    INTO my_table (METER, PDATE, STATUS)
         VALUES ('ABC', '05-Jan-2001', 'off')
    INTO my_table (METER, PDATE, STATUS)
         VALUES ('ABC', '06-Jan-2001', 'off')
    INTO my_table (METER, PDATE, STATUS)
         VALUES ('ABC', '07-Jan-2001', 'on')
    INTO my_table (METER, PDATE, STATUS)
         VALUES ('ABC', '08-Jan-2001', 'on')
    INTO my_table (METER, PDATE, STATUS)
         VALUES ('ABC', '09-Jan-2001', 'off')
SELECT * FROM dual
;

您可以使用名为lag

的分析功能

<强>查询:

SELECT meter, 
       pdate, 
       pdate - Min (pdate) 
                 over( 
                   ORDER BY grp DESC) + 1 AS daysoff 
FROM   (SELECT meter, 
               pdate, 
               status, 
               Max(grp) 
                 over( 
                   ORDER BY pdate) grp 
        FROM   (SELECT meter, 
                       pdate, 
                       status, 
                       CASE 
                         WHEN Lag(status) 
                                over ( 
                                  ORDER BY pdate) != ( status ) THEN 
                         Row_number() 
                         over ( 
                           ORDER BY pdate) 
                         WHEN Row_number() 
                                over ( 
                                  ORDER BY pdate) = 1 THEN 1 
                       END grp 
                FROM   my_table)) 
WHERE  status = 'on' 
ORDER  BY pdate ASC; 

<强>结果:

METER   PDATE   DAYSOFF
ABC January, 02 2001 00:00:00   1
ABC January, 03 2001 00:00:00   2
ABC January, 04 2001 00:00:00   3
ABC January, 07 2001 00:00:00   1
ABC January, 08 2001 00:00:00   2
相关问题