我的桌子:
Date Employee Status
-----------------------------
20171106 001 At work
20171107 001 Sick leave
20171108 001 At work
20171109 001 At work
20171111 001 Sick leave (A gap here)
20171112 001 Sick leave
20171115 001 At work (Another gap)
20171116 001 At work
期望的结果:
Employee Status StartDT EndDT
-------------------------------------------------------------------
001 At work Some time in the history 20171106
001 Sick leave 20171107 20171107
001 At work 20171108 20171109
001 Sick leave 20171111 20171112
001 At work 20171115 20171116
逻辑:我们按状态重新组织源表,而不是按日期。所以日期的差距并不重要,应该被忽略。
如何在 Teradata 15 中执行此操作?
注意:select min(Date), max(Date) group by employee, status;
将不起作用,因为两个“工作中”之间的状态可能会发生变化。
最简单的解决方案是标准化一个周期:
NORMALIZE
是一种非常未知的语法,它结合了重叠的时段,您只需要在日期列之外创建一个一天的时段。由于这会导致您在一段时间内获得稍微不同的输出,因此结束日期与预期结果相比是 +1。要解决此问题,您可以将期间拆分回单独的列:
得到这个历史上的某些时候更复杂,需要额外的计算,你应该检查你是否真的需要它。
另一个更经典的解决方案计算具有连续值的行组:
当您添加时,这两种解决方案都会导致额外的一行
如果要与上一行合并
它也有点复杂......
我无权访问 Teradata 系统(而且我的 Teradata '知识'非常陈旧/过时),因此针对 SQL Server 测试了以下代码。
注意:我假设转换为 Teradata 语法的(相对)小问题......
我们将从表格和示例数据开始:
为了使查询更容易编写,我们将通过添加一些临时的“开始”和“结束”记录来扩展
Employee
我们的源数据。“开始”记录将复制最早
Status
但带有Date=18000101
,而“结束”记录将设置Status='BOGUS'
和Date=99991231
:下一步是使用我们扩展的源数据为每个数据记录提供一个范围。
虽然每个范围的开始只是
Date
,但每个记录的上限将由 a)找到Date
下一个(不同)的Status
,然后 b)从所述 中减去 1 天Date
:最后一部分是将这些范围分组,
maxDate
丢弃Status=BOGUS
记录,并根据问题中显示的所需输出进行一些数据转换以“漂亮地打印”结果。我们确实需要加入我们的扩展数据以获得有效的
EndDt
(ranges.maxDate不一定是有效的日期,因为我们只是减去一天而没有验证所说的日期是实际Date
值):这是上面的小提琴。
可能有一种更有效的方法来做到这一点,在后台凝结后我可能会想到别的东西,但现在我想把这个(蛮力?)想法记下来......也许有人可以把它用作更有效的方法的起点......