我需要连接表A和表B来创建表C.
表A和表B存储ID的状态标志 . 状态标志(A_Flag和B_Flag)可以不时更改,因此一个ID可以包含多行,表示ID状态的历史记录 . 特定ID的标志可以彼此独立地改变,这可以导致表A中的一行属于表B中的多行,反之亦然 .
结果表(表C)需要是唯一日期范围的列表,其中包括ID生命周期中的每个日期(01/01 / 2008-18 / 08/2008),以及每个日期范围的A_Flag和B_Flag值 .
实际表包含数百个ID,每个ID每个表具有不同的行数 .
我可以访问SQL和SAS工具来实现最终结果 .
Source - Table A
ID Start End A_Flag
1 01/01/2008 23/03/2008 1
1 23/03/2008 15/06/2008 0
1 15/06/2008 18/08/2008 1
Source - Table B
ID Start End B_Flag
1 19/01/2008 17/02/2008 1
1 17/02/2008 15/06/2008 0
1 15/06/2008 18/08/2008 1
Result - Table C
ID Start End A_Flag B_Flag
1 01/01/2008 19/01/2008 1 0
1 19/01/2008 17/02/2008 1 1
1 17/02/2008 23/03/2008 1 0
1 23/03/2008 15/06/2008 0 0
1 15/06/2008 18/08/2008 1 1
4 回答
假设你有一个名为
lag
的函数(SQL Server 2012,Oracle,Postgres,DB2),我将在SQL中解决这个问题 . 您可以使用相关子查询获得相同的效果 .想法是获得所有不同的时间段 . 然后加入到原始表中以获取标志 .
我无法上传代码,但可以获得大部分代码 . 但是,它从开始结束开始,您通过在一列中执行四个日期的
union
(而不是union all
)来创建:选择a.start作为日期 . 然后用a.end,b.start和b.end联合起来 .您提出的问题可以在一个没有非标准扩展的SQL语句中解决 .
要认识到的最重要的事情是,开始 - 结束对中的日期每个都代表一个时间 Span 的潜在起点或终点,在此期间标志对将为真 . 实际上,一个日期是"begin"而另一个日期是"end"并不重要;任何日期都是时间分隔符,它同时执行两个操作:它结束前一个时间段并开始另一个时间段 . 构造一组最小时间间隔,并将它们连接到表以查找在每个间隔期间获得的标志 .
我将您的示例(和解决方案)添加到我的Canonical SQL页面 . 请参阅那里进行详细讨论 . 公平对待SO,这是查询本身
一种可能的SAS解决方案是执行部分连接,然后在数据步骤中创建必要的附加行 . 假设tableA具有所有可能的记录,这应该有效;如果不是这种情况(如果tableB可以在tableA之前启动),可能需要一些额外的逻辑来考虑这种可能性(如果first.id和start gt b_start) . 示例数据中不存在的问题可能还需要额外的逻辑 - 我今天早上没有很多时间,并且没有针对示例数据案例之外的任何事情进行调试,但概念应该是显而易见的 .
具有移位和偏移的这种类型的顺序处理是SAS DATA步骤闪耀的情况之一 . 并不是说这个答案很简单,但它比使用SQL更简单,可以这样做,但是并没有考虑到这种顺序处理 .
此外,基于DATA步骤的解决方案往往非常有效 . 这个理论上在时间O(n log n)中运行,但在实践中更接近O(n),并且在恒定空间中 .
前两个DATA步骤只是加载数据,从Joe的答案略微修改,以具有多个ID(否则语法更容易)并添加一些极端情况,即,无法确定初始状态的ID .
下一个数据步骤找到每个id和flag的第一个修改,并将初始值设置为与它找到的相反 .
下一个数据步骤将人工“第一”表与其他两个表合并,保留已知的最后状态并丢弃人工初始行 .
上面的步骤几乎可以做到 . 唯一的错误是结束日期是错误的,因为它们是从原始行复制的 . 要解决此问题,请将下一个开头复制到每一行的末尾,除非它是最后一行 . 最简单的方法是通过反向启动对每个id进行排序,回顾一条记录,然后在最后再次升序 .