我们正在尝试完成一个表,因为我们需要计算数值变量的不同滞后值。我们的表格的一个示例可以是:
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行)来开发这个问题。
由于
答案 0 :(得分:0)
一如既往,您需要两个步骤:
问题在于客户存在月份缺口,因此我们必须创建这些月份。确切地说:对于每个客户,我们想要创建从最小(月)到最大(月)的所有月份。一旦我们为每个客户提供所有月份,我们就可以加入我们的表格。
在查看我的查询之前,您可以在这里停止阅读并尝试自己编写查询。
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 ;