SQL Server查询以可传递关系查找具有可能的循环循环的最小日期

时间:2017-08-07 20:51:27

标签: sql sql-server min transitivity

使用这一个查询和一些提示/建议遇到一点头痛将不胜感激。我找不到任何与我的问题真正相关的东西 - 我在传递闭包上找到了一些东西,这不是我需要的东西,因为我的数据可能会创建一个循环/循环,我认为会导致递归调用无限循环

假设我有两个基本表,其下方显示数据。完全公开:成员资格表CTE已经做了一些逻辑来计算 CServiceDate 列值。 转移表是一个实际的表格,除了PK和 FromMembershipID ToMembershipID 关系之外,它不包含任何其他内容。

成员

 
+==========+==============+================+==============+
| PersonID | MemberShipID | MembershipDate | CServiceDate |
+==========+==============+================+==============+
|        1 |           15 | Aug-01-2016    | Aug-27-2017  |
+----------+--------------+----------------+--------------+
|        1 |           16 | Mar-25-2016    | Sep-01-2000  |
+----------+--------------+----------------+--------------+
|        1 |           17 | Dec-06-2011    | May-15-1995  |
+----------+--------------+----------------+--------------+
|        1 |           18 | Jan-12-2009    | Feb-28-1998  |
+----------+--------------+----------------+--------------+
|        1 |           19 | Apr-08-2016    | Jul-10-1994  |
+----------+--------------+----------------+--------------+
|        1 |           20 | Jun-11-2010    | Nov-12-1997  |
+----------+--------------+----------------+--------------+

转移

+=====+==================+================+
| TID | FromMembershipID | ToMembershipID |
+=====+==================+================+
|   1 |               16 |             15 |
+-----+------------------+----------------+
|   2 |               18 |             17 |
+-----+------------------+----------------+
|   3 |               19 |             17 |
+-----+------------------+----------------+
|   4 |               20 |             18 |
+-----+------------------+----------------+
|   5 |               20 |             19 |
+-----+------------------+----------------+

问题 我需要查询的是成员资格CTE中的每一行(即每个MembershipID),我想为所有相关的MembershipID返回MIN CServiceDate。我将此MIN值称为ECSD(预期信用服务日期)。 ECSD的计算只有两个条件:

  • 如果会员记录通过查看FromMembershipID和ToMembershipID列,通过转移表以某种方式与当前的MembershipID相关联,则会将其视为与当前MembershipID“相关”。例如对于MembershipID 20,如果我们查看转移表,我们可以看到MembershipIDs 20,19,18和17都是通过传递性相关的(除了:通过转移)表,我们可以看到MembershipIDs 15和16彼此相关,但不是[20,19,18,17])
  • 在上面直接派生的与传递相关的会员资格列表中,在计算ECSD时可以在传递关系列表中考虑的唯一成员资格是MembershipDate比当前MembershipID更早的成员资格。例如根据给定MembershipID和转移表的MembershipDates,如果查看MembershipID 17,则在计算MembershiID 17的ECSD时不能考虑MembershipID 19,因为MembershipID 19具有MembershipDate(2016年4月8日) )不早于MembershipID 17(2011年12月6日)
ECSD列的

预期输出

+==========+==============+================+==============+=============+
| PersonID | MemberShipID | MembershipDate | CServiceDate |    ECSD     |
+==========+==============+================+==============+=============+
|        1 |           15 | Aug-01-2016    | Aug-27-2017  | Sep-01-2000 |
+----------+--------------+----------------+--------------+-------------+
|        1 |           16 | Mar-25-2016    | Sep-01-2000  | Sep-01-2000 |
+----------+--------------+----------------+--------------+-------------+
|        1 |           17 | Dec-06-2011    | May-15-1995  | May-15-1995 |
+----------+--------------+----------------+--------------+-------------+
|        1 |           18 | Jan-12-2009    | Feb-28-1998  | Feb-28-1998 |
+----------+--------------+----------------+--------------+-------------+
|        1 |           19 | Apr-08-2016    | Jul-10-1994  | Jul-10-1994 |
+----------+--------------+----------------+--------------+-------------+
|        1 |           20 | Jun-11-2010    | Nov-12-1997  | Nov-12-1997 |
+----------+--------------+----------------+--------------+-------------+

请注意:

  • MembershipID可以在FromMembershipID和ToMembershipID列中显示倍数。这些列值不必是唯一的。
  • (编辑后添加)我希望将此解决方案应用于传输深度超过2层的较大数据集。请参阅下面的示例:

示例2

成员

+==========+==============+================+==============+
| personid | membershipid | membershipdate | CServiceDate |
+==========+==============+================+==============+
|   499510 |       548426 | 2014-09-29     | 2014-09-29   |
+----------+--------------+----------------+--------------+
|   499510 |       548428 | 2014-01-29     | 2014-01-29   |
+----------+--------------+----------------+--------------+
|   499510 |       548425 | 2012-05-28     | 2012-05-28   |
+----------+--------------+----------------+--------------+
|   499510 |       548429 | 2011-11-23     | 2011-11-23   |
+----------+--------------+----------------+--------------+
|   499510 |       548427 | 2008-07-03     | 2008-07-03   |
+----------+--------------+----------------+--------------+
|   499510 |       548431 | 2001-05-01     | 1976-01-01   |
+----------+--------------+----------------+--------------+
|   499510 |       548430 | 1998-10-08     | 1998-10-08   |
+----------+--------------+----------------+--------------+

传输

+=======+========+==================+================+
|  tid  |  pid   | FromMembershipID | ToMembershipID |
+=======+========+==================+================+
| 10664 | 499510 |           548430 |         548431 |
+-------+--------+------------------+----------------+
| 10665 | 499510 |           548431 |         548427 |
+-------+--------+------------------+----------------+
| 10666 | 499510 |           548427 |         548429 |
+-------+--------+------------------+----------------+
| 10667 | 499510 |           548429 |         548425 |
+-------+--------+------------------+----------------+
| 10668 | 499510 |           548425 |         548428 |
+-------+--------+------------------+----------------+
| 10669 | 499510 |           548428 |         548426 |
+-------+--------+------------------+----------------+
| 13085 | 499510 |           548430 |         548427 |
+-------+--------+------------------+----------------+
| 13086 | 499510 |           548427 |         548425 |
+-------+--------+------------------+----------------+
| 13087 | 499510 |           548425 |         548426 |
+-------+--------+------------------+----------------+
| 13088 | 499510 |           548431 |         548429 |
+-------+--------+------------------+----------------+
| 13089 | 499510 |           548429 |         548428 |
+-------+--------+------------------+----------------+

预期结果

+==========+==============+================+==============+============+
| personid | membershipid | membershipdate | CServiceDate |    ECSD    |
+==========+==============+================+==============+============+
|   499510 |       548426 | 2014-09-29     | 2014-09-29   | 1976-01-01 |
+----------+--------------+----------------+--------------+------------+
|   499510 |       548428 | 2014-01-29     | 2014-01-29   | 1976-01-01 |
+----------+--------------+----------------+--------------+------------+
|   499510 |       548425 | 2012-05-28     | 2012-05-28   | 1976-01-01 |
+----------+--------------+----------------+--------------+------------+
|   499510 |       548429 | 2011-11-23     | 2011-11-23   | 1976-01-01 |
+----------+--------------+----------------+--------------+------------+
|   499510 |       548427 | 2008-07-03     | 2008-07-03   | 1976-01-01 |
+----------+--------------+----------------+--------------+------------+
|   499510 |       548431 | 2001-05-01     | 1976-01-01   | 1976-01-01 |
+----------+--------------+----------------+--------------+------------+
|   499510 |       548430 | 1998-10-08     | 1998-10-08   | 1998-10-08 |
+----------+--------------+----------------+--------------+------------+

请注意:我已在传输表中添加了pid列。忘了第一次包括那个。

谢谢!

1 个答案:

答案 0 :(得分:1)

我认为这是准确的结果......虽然它与您的预期不同。请参阅上面的评论。

由于第一个表是CTE,您可以将其保留为CTE并继续使用此CTE或将这些结果存储到临时表中,这可能是有益的,因为我们将多次查询它。

declare @Memberships table (PersonID int, MemberShipID int, MembershipDate varchar(16), CServiceDate varchar(16))
insert into @Memberships
values
(1,15,'Aug-01-2016','Aug-27-2017'),
(1,16,'Mar-25-2016','Sep-01-2000'),
(1,17,'Dec-06-2011','May-15-1995'),
(1,18,'Jan-12-2009','Feb-28-1998'),
(1,19,'Apr-08-2016','Jul-10-1994'),
(1,20,'Jun-11-2010','Nov-12-1997') 

declare @Transfer table (TID int, FromMembershipID int, ToMembershipID int)
insert into @Transfer
values
(1,16,15),
(2,18,17),
(3,19,17),
(4,20,18),
(5,20,19)

;with cte as(
select
    m.PersonID
    ,m.MemberShipID
    ,m.MembershipDate
    ,m.CServiceDate
    ,case when t.FromMembershipID <> m.MemberShipID then t.FromMembershipID else t.ToMembershipID end RelatedMemberShips
from
    @Memberships m
    left join
    @Transfer t on
    t.FromMembershipID = m.MemberShipID or t.ToMembershipID = m.MemberShipID)

select distinct
    cte.PersonID
    ,cte.MemberShipID
    ,cte.MembershipDate
    ,cte.CServiceDate
    --,cte.RelatedMemberShips
    --,m.MembershipDate
    --,m.CServiceDate
    ,case when min(convert(date, replace(m.CServiceDate, '-', ' '), 0)) over (partition by cte.MemberShipID) < convert(date, replace(cte.CServiceDate, '-', ' '), 0) then min(convert(date, replace(m.CServiceDate, '-', ' '), 0)) over (partition by cte.MemberShipID)  else convert(date, replace(cte.CServiceDate, '-', ' '), 0) end
from 
    cte
    left join 
        @Memberships m on m.MemberShipID = cte.RelatedMemberShips
        and convert(date, replace(m.MembershipDate, '-', ' '), 0) <= convert(date, replace(cte.MembershipDate, '-', ' '), 0)

或者,你可以写这个IN LINE并一起跳过CTE ......

select distinct
    m.PersonID
    ,m.MemberShipID
    ,m.MembershipDate
    ,m.CServiceDate
    --,case when t.FromMembershipID <> m.MemberShipID then t.FromMembershipID else t.ToMembershipID end RelatedMemberShips
    ,case when min(convert(date, replace(m2.CServiceDate, '-', ' '), 0)) over (partition by m.MemberShipID) < convert(date, replace(m.CServiceDate, '-', ' '), 0) then min(convert(date, replace(m2.CServiceDate, '-', ' '), 0)) over (partition by m.MemberShipID)  else convert(date, replace(m.CServiceDate, '-', ' '), 0) end
from
    @Memberships m
    left join
    @Transfer t on
    t.FromMembershipID = m.MemberShipID or t.ToMembershipID = m.MemberShipID
    left join 
        @Memberships m2 on m2.MemberShipID = case when t.FromMembershipID <> m.MemberShipID then t.FromMembershipID else t.ToMembershipID end
        and convert(date, replace(m2.MembershipDate, '-', ' '), 0) <= convert(date, replace(m.MembershipDate, '-', ' '), 0)