【发布时间】:2014-05-16 05:55:52
【问题描述】:
我在 Teradata 中遇到一个问题,我正在尝试构建一个历史合同表,其中列出了一个系统、相应的合同以及每个合同的开始和结束日期。然后将查询该表以作为时间点表进行报告。这里有一些代码可以更好地解释。
CREATE TABLE TMP_WORK_DB.SOLD_SYSTEMS
(
SYSTEM_ID varchar(5),
CONTRACT_TYPE varchar(10),
CONTRACT_RANK int,
CONTRACT_STRT_DT date,
CONTRACT_END_DT date
);
INSERT INTO TMP_WORK_DB.SOLD_SYSTEMS VALUES ('AAA', 'BEST', 10, '2012-01-01', '2012-06-30');
INSERT INTO TMP_WORK_DB.SOLD_SYSTEMS VALUES ('AAA', 'BEST', 9, '2012-01-01', '2012-06-30');
INSERT INTO TMP_WORK_DB.SOLD_SYSTEMS VALUES ('AAA', 'OK', 1, '2012-08-01', '2012-12-30');
INSERT INTO TMP_WORK_DB.SOLD_SYSTEMS VALUES ('BBB', 'BEST', 10, '2013-12-01', '2014-03-02');
INSERT INTO TMP_WORK_DB.SOLD_SYSTEMS VALUES ('BBB', 'BETTER', 7, '2013-12-01', '2017-03-02');
INSERT INTO TMP_WORK_DB.SOLD_SYSTEMS VALUES ('BBB', 'GOOD', 4, '2016-12-02', '2017-12-02');
INSERT INTO TMP_WORK_DB.SOLD_SYSTEMS VALUES ('CCC', 'BEST', 10, '2009-10-13', '2014-10-14');
INSERT INTO TMP_WORK_DB.SOLD_SYSTEMS VALUES ('CCC', 'BETTER', 7, '2009-10-13', '2016-10-14');
INSERT INTO TMP_WORK_DB.SOLD_SYSTEMS VALUES ('CCC', 'OK', 2, '2008-10-13', '2017-10-14');
所需的输出是:
SYSTEM_ID CONTRACT_TYPE CONTRACT_STRT_DT CONTARCT_END_DT CONTRACT_RANK
AAA BEST 01/01/2012 06/30/2012 10
AAA OK 08/01/2012 12/30/2012 1
BBB BEST 12/01/2013 03/02/2014 10
BBB BETTER 03/03/2014 03/02/2017 7
BBB GOOD 03/03/2017 12/02/2017 4
CCC OK 10/13/2008 10/12/2009 2
CCC BEST 10/13/2009 10/14/2014 10
CCC BETTER 10/15/2014 10/14/2016 7
CCC OK 10/15/2016 10/14/2017 2
我不一定要减少行数,而是希望在任何给定时间点获得 system_id 的正确状态。请注意,当排名较高的合约结束且排名较低的合约仍处于活动状态时,排名较低的合约会在排名较高的合约停止的地方接手。
我们使用的是 TD 14,我已经能够获得日期顺序流动且排名较高的简单记录,但在两个不同排名的合同涵盖多个日期跨度的重叠时遇到了问题。
我找到了这篇博文 (Sharpening Stones) 并且大部分时间都能正常工作,但我仍然无法为重叠合同设置新的开始日期。
任何帮助将不胜感激。谢谢。
*2014 年 4 月 4 日更新 *
我想出了以下代码,这正是我想要的,但我不确定性能。它适用于几百行的较小数据集,但我还没有在几百万行上测试过:
*2014 年 4 月 7 日更新 * 由于假脱机问题更新了日期子查询。此查询会在合约可能处于活动状态的所有日子里爆炸,然后使用 ROW_NUMBER 函数来获取每天排名最高的 CONTRACT_TYPE。然后将 MIN/MAX 函数划分为系统和合约类型,以便在排名最高的合约类型发生变化时获取。
*更新 - 2 - 04/07/2014 * 我清理了查询,它似乎执行得更好一些。
SELECT
SYSTEM_ID
, CONTRACT_TYPE
, MIN(CALENDAR_DATE) NEW_START_DATE
, MAX(CALENDAR_DATE) NEW_END_DATE
, CONTRACT_RANK
FROM (
SELECT
CALENDAR_DATE
, SYSTEM_ID
, CONTRACT_TYPE
, CONTRACT_RANK
, ROW_NUMBER() OVER (PARTITION BY SYSTEM_ID, CALENDAR_DATE ORDER BY CONTRACT_RANK DESC, CONTRACT_STRT_DT DESC, CONTRACT_END_DT DESC) AS RNK
FROM SOLD_SYSTEMS t1
JOIN (
SELECT CALENDAR_DATE
FROM FULL_CALENDAR_TABLE ia
WHERE CALENDAR_DATE > DATE'2013-01-01'
)dt
ON CALENDAR_DATE BETWEEN CONTRACT_STRT_DT AND CONTRACT_END_DT
QUALIFY RNK = 1
)z1
GROUP BY 1,2,5
【问题讨论】:
-
在此期间内的每一天将数据分解为一行也可以使用 EXPAND ON 完成,但您最终会得到一个潜在的巨大线轴。您是否需要编写一个查询来获得该结果?还是一个带有易失性表的两步过程也可以?我有一个类似的问题,我可以重写......
-
你对爆炸数据的看法是对的,它需要一个巨大的线轴。我可以使用尽可能多的步骤和任何方法来完成这项工作。 @rpc1 的答案很接近,但仍然存在未选择正确结束日期的情况。我目前正在努力解决这个问题。
-
我清理了
CREATE TABLE和INSERT数据语句,以使用@dnoeth 的解决方案提供一个完全有效的解决方案。很棒的工作。
标签: sql date teradata gaps-and-islands