如何在SQL-oracle中为两个日期之间的所有月份获取不同客户端的不同范围

时间:2017-11-02 11:54:37

标签: sql oracle

我们正在尝试完成一个表,因为我们需要计算数值变量的不同滞后值。我们的表格的一个示例可以是:

CLIENT MONTH    VARIABLE
1      201705   45
1      201706   33
2      201603   11
2      201605   22
2      201606   12

我需要完成表格,添加缺少月份的行和数值变量的空值。在我们的例子中,理想的解决方案是:

CLIENT MONTH    VARIABLE
1      201705   45
1      201706   33
2      201603   11
2      201604   NULL
2      201605   22
2      201606   12

提示(或不......):

为了解决这个问题,我们能够:

1)获取两个给定日期之间的所有月份:

SELECT TO_CHAR(ADD_MONTHS(TO_DATE('201304', 'YYYYMM'), LEVEL - 1), 'YYYYMM') MONTHS
FROM DUAL
CONNECT BY LEVEL <= MONTHS_BETWEEN(TO_DATE('201708', 'YYYYMM'), TO_DATE('201304', 'YYYYMM'));

2)获取每个客户的最短和最长日期:

SELECT CLIENT, MIN(MONTH) AS MIN_MONTH, MAX(MONTH) AS MAX_MONTH FROM TABLE1 GROUP BY CLIENT;
PD:我们正试图用非常庞大的数据集(+300.000.000行)来开发这个问题。

由于

2 个答案:

答案 0 :(得分:0)

一如既往,您需要两个步骤:

  1. 找到算法
  2. 撰写查询
  3. 第1步

    问题在于客户存在月份缺口,因此我们必须创建这些月份。确切地说:对于每个客户,我们想要创建从最小(月)到最大(月)的所有月份。一旦我们为每个客户提供所有月份,我们就可以加入我们的表格。

    在查看我的查询之前,您可以在这里停止阅读并尝试自己编写查询。

    第2步

    with pairs (client, month) as
    (
      select client, min(month) as month from mytable group by client
      union all
      select client, case when mod(month, 12) > 0 then month + 1 else month + 89 end as month
      from pairs
      where month < (select max(m.month) from mytable m where m.client = pairs.client)
    )
    select client, month, m.variable
    from pairs 
    left join mytable m using (client, month)
    order by client, month;
    

    我在这里使用标准SQL的递归CTE而不是Oracle的CONNECT BY。它做同样的事情。您可能希望将我的查询翻译为CONNECT BY查询以进行练习。但通常我建议编写标准SQL查询而不是仅使用Oracle的查询。

答案 1 :(得分:0)



   有时我会得到非常相似的要求,即使没有数据存在,客户也希望看到日期或时间段出现。如果SQL代码在查询数百万条记录的函数中频繁运行,我通常会选择简单的交叉连接到虚拟表并保留连接数据表以提高性能。

SELECT 
TBL1.CLIENT_ID,
TBL1.MONTHS,
YOUR_FIRST_TBL.VARIABLE 

      FROM (SELECT DISTINCT CLIENT_ID,
            TO_CHAR(ADD_MONTHS(TO_DATE('201304', 'YYYYMM'), LEVEL - 1), 'YYYYMM') MONTHS 
            FROM DUAL

            CROSS JOIN (SELECT DISTINCT CLIENT_ID FROM TABLE1) -- DISTINCT LIST OF CLIENT

            CONNECT BY LEVEL <= MONTHS_BETWEEN(TO_DATE('201708', 'YYYYMM'), TO_DATE('201304', 'YYYYMM'))) TBL1

LEFT JOIN (SELECT CLIENT, MIN(MONTH) AS MIN_MONTH, MAX(MONTH) AS MAX_MONTH FROM TABLE1 GROUP BY CLIENT) TBL2
ON TBL1.CLIENT_ID  = TBL2.CLIENT 

LEFT JOIN YOUR_FIRST_TBL -- THE FIRST TABLE WITH THE GAPS
ON TBL1.CLIENT_ID = YOUR_FIRST_TBL.CLIENT 
AND TBL1.MONTHS = YOUR_FIRST_TBL.MONTH

WHERE TBL1.MONTHS BETWEEN TO_CHAR(TBL2.MIN_MONTH,'YYYYMM') AND TO_CHAR(TBL2.MAX_MONTH,'YYYYMM')

ORDER BY TBL1.CLIENT_ID, TBL1.MONTHS ;