在子查询中迭代
Posted
技术标签:
【中文标题】在子查询中迭代【英文标题】:Iterate inside subquery 【发布时间】:2015-02-24 11:18:08 【问题描述】:我有一种情况,我需要根据 where 子句输入来迭代一个条件,想知道如何做到这一点。
背景是,在一个存储单元中,它由几个罐组成,每个罐都有自己的罐倾角测量状态,最后测量的日期不同,我怎样才能得到特定日期的最新倾角值?
例子:
Tank A having last measured date (EndDate) as 01 Feb 2015.
Tank B having last measured date (EndDate) as 31 Jan 2015.
Tank B having last measured date (EndDate) as 17 Feb 2015.
Tank C having last measured date (EndDate) as 18 Feb 2015.
表结构:
Tanks| DipDaytime| Volume| EndDate
A, 28 Jan 2015 8pm, 1000, 01 Feb 2015
B, 30 Jan 2015 5pm, 2000, 31 Jan 2015
B, 01 Feb 2015 5pm, 2500, 17 Feb 2015
C, 01 Feb 2015 3pm, 3000, 18 Feb 2015
预期的输出是:
For 31 Jan 2015:
A, 28 Jan 2015 8pm, 1000, 01 Feb 2015
B, 30 Jan 2015 5pm, 2000, 31 Jan 2015
For 18 Feb 2015:
A, 28 Jan 2015 8pm, 1000, 01 Feb 2015
B, 01 Feb 2015 5pm, 2500, 17 Feb 2015
C, 01 Feb 2015 3pm, 3000, 18 Feb 2015
我能得出这样的结果:
SELECT ts.Tanks, ts.DipDaytime, ts.EndDate , ts.Volume
FROM table ts
WHERE ts.EndDate =
(SELECT MAX(ts2.EndDate ) FROM table ts2
WHERE ts2.Tanks = ts.Tanks
AND ts2.EndDate <= '17.02.2014')
问题是我需要更改 17.01.2014 每次我需要一个特定日期的不同结果时,我也无法将 17.01.2014 显示为查询结果,因为它不是表的一部分。
如何以动态方式获得它,我只需要提供一个日期范围,例如从 01.02.2014 到 28.02.2014,以获得完整的结果?还有一个显示报告日期的临时列?
最终结果将是:
For 31 Jan 2015:
Tank | DipDaytime| Volume| EndDate, ReportDate
A, 28 Jan 2015 8pm, 1000, 01 Feb 2015, 31 Jan 2015
B, 30 Jan 2015 5pm, 2000, 31 Jan 2015, 31 Jan 2015
For 18 Feb 2015:
Tank | DipDaytime| Volume| EndDate, ReportDate
A, 28 Jan 2015 8pm, 1000, 01 Feb 2015, 18 Feb 2015
B, 01 Feb 2015 5pm, 2500, 17 Feb 2015, 18 Feb 2015
C, 01 Feb 2015 3pm, 3000, 18 Feb 2015, 18 Feb 2015
如果有人可以提供帮助,我们将不胜感激。谢谢。
【问题讨论】:
为什么坦克 A 出现在这两种情况下? @SagarJoon: 因为坦克 A 在 31.01 和 18.02 之间没有变化? 【参考方案1】:您可以使用row_number()
过滤每个(Tank)
组的最新行。使用子查询技巧,您可以使用常量进行过滤和显示:
select *
from (
cross join
(
select row_number() over (
partition by tank
order by EndDate desc) as rn
, *
from Tanks
cross join
(
select '17.02.2014' as report_date
from dual
) pars
where EndDate <= report_date
) numbered_rows
where rn = 1 -- Latest row per tank
使用常量的更好方法是将其作为参数传递给查询。在 Oracle 中,参数以分号开头,例如 :report_date
。
【讨论】:
【参考方案2】:你可以试试这样的:
with report_dates as
(
select to_date('31 Jan 2015', 'dd Mon yyyy') report_date from dual
union all
select to_date('18 Feb 2015', 'dd Mon yyyy') report_date from dual
)
select t.tanks, d.report_date,
max(t.dipdaytime) keep (dense_rank last order by t.enddate) DipDaytime,
max(t.volume) keep (dense_rank last order by t.enddate) volume,
max(t.enddate) enddate
from table t, report_dates d
where t.dipdaytime <= d.report_date
group by t.tanks, d.report_date
order by d.report_date, t.tanks
;
这给出了:
TANKS VOLUME REPORT_DATE DIPDAYTIME ENDDATE
1 A 1000 31-janv.-2015 28-janv.-2015 01-févr.-2015
2 B 2000 31-janv.-2015 30-janv.-2015 31-janv.-2015
3 A 1000 18-févr.-2015 28-janv.-2015 01-févr.-2015
4 B 2500 18-févr.-2015 01-févr.-2015 17-févr.-2015
5 C 3000 18-févr.-2015 01-févr.-2015 18-févr.-2015
【讨论】:
【参考方案3】:我倾向于对您的查询稍作修改——定义您想要的日期列表,然后在查询中多次使用该列表:
with rd as (
select date '2015-01-31' as ReportDate from dual
union all
select date '2015-02-18' from dual
)
select rd.ReportDate, t.*
from table t cross join
rd
where t.dipdaytime = (select max(t2.dipdaytime)
from table t2
where t2.tank = t.tank and
t2.dipdaytime <= rd.ReportDate
);
这是使用相关子查询来查找每个罐在任何给定日期之前的最大 dipdaytime
。
【讨论】:
【参考方案4】:此查询给出选定时间段的结果。您必须在第一个子查询 (dates
) 中定义开始报告日期和最后日期。
with dates as (
select to_date('2015-02-15') + level - 1 tdate
from dual
connect by to_date('2015-02-15') + level - 1 <= '2015-02-18'),
tanks as (
select *
from (
select tdate, tanks, dipdaytime, volume, enddate
row_number() over (partition by tanks, tdate order by enddate desc) rn
from dates
left join ts on ts.enddate <= dates.tdate)
where rn = 1)
select tdate, tanks, dipdaytime, volume
from tanks
order by tdate, tanks
TDATE TANKS DIPDAYTIME VOLUME
----------- ---------- ----------- ----------
2015-02-15 A 2015-01-28 1000
2015-02-15 B 2015-01-30 2000
2015-02-16 A 2015-01-28 1000
2015-02-16 B 2015-01-30 2000
2015-02-17 A 2015-01-28 1000
2015-02-17 B 2015-02-01 2500
2015-02-18 A 2015-01-28 1000
2015-02-18 B 2015-02-01 2500
2015-02-18 C 2015-02-01 3000
9 rows selected
下面的查询会在不同的列中选择每个罐的数据,因此您在报告期间的每个日期都有一行。
with dates as (
select to_date('2015-02-15') + level - 1 tdate
from dual
connect by to_date('2015-02-15') + level - 1 <= '2015-02-18'),
tanks as (
select *
from (
select tdate, tanks, dipdaytime, volume, enddate,
row_number() over (partition by tanks, tdate order by enddate desc) rn
from dates
left join ts on ts.enddate <= dates.tdate)
where rn = 1)
select dates.tdate,
ta.dipdaytime a_ddt, ta.volume a_vol, ta.enddate a_end,
tb.dipdaytime b_ddt, tb.volume b_vol, tb.enddate b_end,
tc.dipdaytime c_ddt, tc.volume c_vol, tc.enddate c_end
from dates
left join tanks ta on ta.tdate = dates.tdate and ta.tanks = 'A'
left join tanks tb on tb.tdate = dates.tdate and tb.tanks = 'B'
left join tanks tc on tc.tdate = dates.tdate and tc.tanks = 'C'
order by tdate
结果:
TDATE A_DDT A_VOL A_END B_DDT B_VOL B_END C_DDT C_VOL C_END
----------- ----------- ---------- ----------- ----------- ---------- ----------- ----------- ---------- -----------
2015-02-15 2015-01-28 1000 2015-02-01 2015-01-30 2000 2015-01-31
2015-02-16 2015-01-28 1000 2015-02-01 2015-01-30 2000 2015-01-31
2015-02-17 2015-01-28 1000 2015-02-01 2015-02-01 2500 2015-02-17
2015-02-18 2015-01-28 1000 2015-02-01 2015-02-01 2500 2015-02-17 2015-02-01 3000 2015-02-18
【讨论】:
以上是关于在子查询中迭代的主要内容,如果未能解决你的问题,请参考以下文章