在子查询中迭代

时间:2015-02-24 11:18:08

标签: sql oracle subquery

我有一种情况,我需要根据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 作为其中一部分查询结果,因为它不是表的一部分。

从2014年2月1日到2014年2月28日,我怎样才能以动态的方式提供日期范围,以获得完整的结果?和一个显示报告日期的临时栏?

最终结果将是:

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 

赞赏是否有人可以提供帮助。感谢。

4 个答案:

答案 0 :(得分:0)

您可以使用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

答案 1 :(得分:0)

您可以尝试以下内容:

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

答案 2 :(得分:0)

我倾向于对您的查询稍作修改 - 定义您想要的日期列表,然后在查询中多次使用该列表:

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

答案 3 :(得分:0)

此查询提供所选期间的结果。您必须在第一个子查询(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