我需要查询以获取上次仪表上线的天数。 例如:
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
答案 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)
将包含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