【问题标题】:Merging Respective Start and End Dates, Setting Flag Depending on Start/End Date - SQL合并各自的开始日期和结束日期,根据开始/结束日期设置标志 - SQL
【发布时间】:2022-02-03 09:34:13
【问题描述】:

问题: 我有一个交易表(见下文),其中包含日期格式的(打开/开始)或(关闭/结束)交易。任务是将这些交易与其相应的日期合并,但是,也可能存在交易已打开/开始但未关闭/结束的情况,在这种情况下,只需显示开始日期,并分配标志“Y” .其余有结束日期的情况,标志将为“N”。我已经能够根据 first_value(Flag IGNORE NULLS) over(按 Customer_ID 顺序按 EndDate desc 进行分区) 的值设置一个标志,但是我很难将开始日期与结束日期匹配。

逻辑:

  • 一天可以有多个开始和结束日期,只有时差。
  • 每位客户每天只能进行一次开始和结束日期交易。
  • 每个客户每天只能有一个开始日期(打开)交易。
  • 标记“N”仅用于已关闭的交易,“Y”用于打开,即没有结束日期。

当前设置示例如下所示: 注意:对于这个特定的客户,我们有相同数量的交易(插入和删除,所以我知道标志将是'N',当我们映射开始和结束日期时,但是当开始日期多于结束日期时,解决方案也必须工作其他客户,其中最终(最新)开始日期行的标志为“Y”。

Customer_Id Start Date End Date Flag
111 02/07/2020 20:58:32.000000 N
111 02/07/2020 19:18:04.000000 N
111 01/06/2020 09:38:49.000000 N
111 01/06/2020 09:36:34.000000 N
111 29/05/2020 16:58:07.000000 N
111 02/07/2020 20:57:52.000000 N
111 02/07/2020 19:17:22.000000 N
111 29/05/2020 16:58:06.000000 N
111 01/06/2020 09:38:40.000000 N
111 01/06/2020 09:36:34.000000 N

预期结果:

Customer_Id Start Date End Date Flag
111 02/07/2020 20:57:52.000000 02/07/2020 20:58:32.000000 N
111 02/07/2020 19:17:22.000000 02/07/2020 19:18:04.000000 N
111 01/06/2020 09:38:40.000000 01/06/2020 09:38:49.000000 N
111 01/06/2020 09:36:34.00000 01/06/2020 09:36:34.000000 N
111 29/05/2020 16:58:06.000000 29/05/2020 16:58:07.000000 N

适用于上述示例的部分解决方案:

select customer_id
  ,start_date
  ,lead(end_date) -- find the next row's end date
   over (partition by customer_id
         order by coalesce(start_date, end_date)) as new_end
  ,case when new_end is null then 'Y' else 'N' end as flag
from tab
qualify start_date is not null -- only return starting rows
order by 1,2;

但是,如果开始日期多于结束日期(例如,几个已关闭的交易,一个打开的交易(不仅可以是最新/最新的,还可以是中间的某个地方..)),它将不起作用。例如。如果交易未完成:开始日期 12/01/2019 09:36:34.00000 但没有结束日期 然后 lead() 跳过这个 null enddate 值并分配下一个可用的结束日期,使解决方案看起来像这样:

Customer_Id Start Date End Date Flag
111 02/07/2020 20:57:52.000000 02/07/2020 20:58:32.000000 N
111 02/07/2020 19:17:22.000000 02/07/2020 19:18:04.000000 N
111 01/06/2020 09:38:40.000000 01/06/2020 09:38:49.000000 N
111 12/01/2019 09:36:34.00000 18/11/2019 16:58:07.000000 N
111 18/11/2019 16:58:06.000000 NULL NULL

什么时候应该是这样的:

Customer_Id Start Date End Date Flag
111 02/07/2020 20:57:52.000000 02/07/2020 20:58:32.000000 N
111 02/07/2020 19:17:22.000000 02/07/2020 19:18:04.000000 N
111 01/06/2020 09:38:40.000000 01/06/2020 09:38:49.000000 N
111 12/01/2019 09:36:34.00000 NULL Y
111 18/11/2019 16:58:06.000000 18/11/2019 16:58:07.000000 N

如何找到适合这两种情况的解决方案?

【问题讨论】:

  • 您想匹配 DATE 和 TIMESTAMP?您的预期结果是什么,为什么?
  • 将两者都更改为时间戳,添加预期结果。预期结果的原因 - 所有交易都需要输入一行,并带有一个标志。
  • 结果与数据不匹配,NULL和上一行将被切换。
  • 它确实如此,这正是我在尝试对一个缺失的结束日期应用相同逻辑时得到的结果(这种情况下缺失的一个将是第 4 行(与 01/06/2020 09:36 无关) :34,但脚本会跳过这条缺失的记录并导入下一条 (29/05..)
  • 这个结果是不可能的,29/05/2020 早于 01/06/2020。一定是别的东西。顺便说一句,如果您的开始日期和结束日期具有完全相同的时间戳,则可能需要添加逻辑首先要排序的内容,例如order by coalesce(start_date, end_date), start_date nulls first)

标签: sql date merge teradata


【解决方案1】:

这将返回每个开始日期的匹配结束日期。

select customer_id
  ,start_date
  ,lead(end_date) -- find the next row's end date
   over (partition by customer_id
         order by coalesce(start_date, end_date)) as new_end
  ,case when new_end is null then 'Y' else 'N' end as flag
from tab
qualify start_date is not null -- only return starting rows
order by 1,2;

【讨论】:

  • 非常感谢!它完美无缺:) :)
  • 对于开始日期多于结束日期的这种情况,您有什么建议吗?在这种情况下,lead 盲目地继续移动 NULL enddate 并分配下一个可用的 enddate,从而弄乱了真实的交易时间戳
  • 是的,如果开始的次数多于结束的次数,则在潜在客户查找下一行的结束时间戳时可能会出现间隙。如果您想要不同的结果,您应该使用这些边缘案例扩展您的示例数据并详细说明逻辑。编辑您的问题或提出新问题。