如何仅通过Oracle

时间:2018-04-19 14:26:21

标签: sql oracle group-by contiguous partition-by

我正在尝试显示每个费率以及开始和结束日期的笼子数量。这是一张发票,我们每天为每个笼子开帐单,这些笼子可以有不同的价格。

这不是一个简单的GROUP BY并获得MINMAX日期,因为网箱的数量可能会下降或上升,然后再次返回相同的数字,所以我需要只查看连续的数据。

我搜索了一个解决方案并找到this answer。我根据自己的需要对其进行了一些修改,然后想出了this

WITH cte(rate_name, cages, use_date) AS (
  SELECT 'I1',      8, DATE'2017-11-04' FROM DUAL UNION ALL
  SELECT 'I1',      8, DATE'2017-11-05' FROM DUAL UNION ALL
  SELECT 'I1',      7, DATE'2017-11-07' FROM DUAL UNION ALL
  SELECT 'I1',      7, DATE'2017-11-10' FROM DUAL UNION ALL
  SELECT 'I1',      7, DATE'2017-11-11' FROM DUAL UNION ALL
  SELECT 'I1',      8, DATE'2017-11-12' FROM DUAL UNION ALL
  SELECT 'I1',      8, DATE'2017-11-13' FROM DUAL UNION ALL
  SELECT 'I1',      8, DATE'2017-11-14' FROM DUAL UNION ALL
  SELECT 'I1 - BR', 5, DATE'2017-11-01' FROM DUAL UNION ALL
  SELECT 'I1 - BR', 5, DATE'2017-11-02' FROM DUAL UNION ALL
  SELECT 'I1 - BR', 5, DATE'2017-11-03' FROM DUAL UNION ALL
  SELECT 'I1 - BR', 5, DATE'2017-11-04' FROM DUAL UNION ALL
  SELECT 'I1 - BR', 5, DATE'2017-11-05' FROM DUAL UNION ALL
  SELECT 'I1 - BR', 1, DATE'2017-11-06' FROM DUAL UNION ALL
  SELECT 'I1 - BR', 1, DATE'2017-11-07' FROM DUAL UNION ALL
  SELECT 'I1 - BR', 1, DATE'2017-11-08' FROM DUAL UNION ALL
  SELECT 'I1 - BR', 5, DATE'2017-11-09' FROM DUAL UNION ALL
  SELECT 'I1 - BR', 5, DATE'2017-11-10' FROM DUAL UNION ALL
  SELECT 'I1 - BR', 5, DATE'2017-11-11' FROM DUAL
)

SELECT
  a.rate_name,
  a.cages,
  MIN(a.use_date) AS startdate,
  MAX(a.use_date) AS enddate
FROM (
  SELECT
    cte.use_date,
    cte.rate_name,
    cte.cages,
    ROW_NUMBER() OVER (ORDER BY cte.rate_name, cte.use_date) - ROW_NUMBER() OVER (PARTITION BY cte.rate_name, cte.cages ORDER BY cte.use_date) AS grp
  FROM cte
) a
GROUP BY a.rate_name, a.cages, a.grp
ORDER BY a.rate_name ASC, 3;

我在PL / SQL中运行了这个查询,它似乎完全符合我的目的。当我尝试使用我们正在使用的软件工具中插入解决方案时,它不支持ROW_NUMBER()OVERPARTITION BY

有没有办法可以在不使用这些内置功能的情况下获得相同的结果?

我开始考虑手动实施ROW_NUMBER()并找到this方法。它似乎在我测试时工作,但我还没有插入它。我现在有点卡住实现PARTITION BY而我只是觉得有点迷失,不知道我是否正在进入正确的方向在这里。

修改

我刚注意到查询返回的结果不正确。

对于I17网箱,应该返回2行。第一行从2017-11-07开始和结束,第二行从2017-11-10开始,到2017-11-11结束。

1 个答案:

答案 0 :(得分:1)

嗯,这有点尴尬,但这是我想到的第一件事。我确定它可以清理干净。我不得不做一个二级CTE,让它在没有任何功能或任何东西的情况下工作。使用connect bylag可能会更容易,但我猜测您的软件工具也无法处理这些问题。

WITH cte(rate_name, cages, use_date) AS (
  SELECT 'I1',      8, DATE'2017-11-04' FROM DUAL UNION ALL
  SELECT 'I1',      8, DATE'2017-11-05' FROM DUAL UNION ALL
  SELECT 'I1',      7, DATE'2017-11-07' FROM DUAL UNION ALL
  SELECT 'I1',      7, DATE'2017-11-10' FROM DUAL UNION ALL
  SELECT 'I1',      7, DATE'2017-11-11' FROM DUAL UNION ALL
  SELECT 'I1',      8, DATE'2017-11-12' FROM DUAL UNION ALL
  SELECT 'I1',      8, DATE'2017-11-13' FROM DUAL UNION ALL
  SELECT 'I1',      8, DATE'2017-11-14' FROM DUAL UNION ALL
  SELECT 'I1 - BR', 5, DATE'2017-11-01' FROM DUAL UNION ALL
  SELECT 'I1 - BR', 5, DATE'2017-11-02' FROM DUAL UNION ALL
  SELECT 'I1 - BR', 5, DATE'2017-11-03' FROM DUAL UNION ALL
  SELECT 'I1 - BR', 5, DATE'2017-11-04' FROM DUAL UNION ALL
  SELECT 'I1 - BR', 5, DATE'2017-11-05' FROM DUAL UNION ALL
  SELECT 'I1 - BR', 1, DATE'2017-11-06' FROM DUAL UNION ALL
  SELECT 'I1 - BR', 1, DATE'2017-11-07' FROM DUAL UNION ALL
  SELECT 'I1 - BR', 1, DATE'2017-11-08' FROM DUAL UNION ALL
  SELECT 'I1 - BR', 5, DATE'2017-11-09' FROM DUAL UNION ALL
  SELECT 'I1 - BR', 5, DATE'2017-11-10' FROM DUAL UNION ALL
  SELECT 'I1 - BR', 5, DATE'2017-11-11' FROM DUAL
),
recur as (
  SELECT
    c1.use_date,
    c1.rate_name,
    c1.cages, 
    case when c3.rate_name is null then c1.use_date else null end as start_date,
    case when c2.rate_name is null then c1.use_date else null end as end_date
  FROM cte c1
  -- next day
  left join cte c2 on c2.rate_name = c1.rate_name and c2.use_date = c1.use_date +1 and c2.cages = c1.cages
  -- prev day
  left join cte c3 on c3.rate_name = c1.rate_name and c3.use_date = c1.use_date -1 and c3.cages = c1.cages
)
select rate_name, cages, start_date, 
    (select min(e.end_date) from recur e
        where e.rate_name = s.rate_name
          and e.end_date >= s.start_date) as end_date
from recur s
where start_date is not null
order by rate_name, start_date;