SQL 服务器查询以查找与/可能循环的传递关系中的最小日期
SQL Server Query to find min date in a transitive relationship w/ possible cyclic loops
运行 对这个查询有点头疼,一些 hints/suggestions 将不胜感激。我找不到任何与我的问题真正相关的东西——我发现了一些关于传递闭包的东西,这不是我所需要的,因为我的数据可能会创建一个 loop/cycle,我认为这会导致递归调用无限循环。
假设我有两个基本 table,数据显示在它们下方。全面披露:Memberships table 一个 CTE 已经做了相当多的逻辑来计算 CServiceDate 列值。 Transfers table 是一个实际的 table,除了 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 |
+-----+------------------+----------------+
问题
我需要查询的是 Memberships CTE 中的每一行(即每个 MembershipID),我想 return 所有相关 MembershipID 的 MIN CServiceDate。我将此最小值称为 ECSD(预期信用服务日期)。 ECSD的计算只有两个条件:
- 如果会员记录通过 Transfers table 通过查看FromMembershipID 和 ToMembershipID 列。例如对于 MembershipID 20,如果我们查看 Transfers table,我们可以看到 MembershipID 20、19、18 和 17 都是通过传递性相关的(此外:通过 Transfers table,我们可以看到MembershipIDs 15和16相互关联,但与[20,19,18,17] )
无关
- 在上面直接派生的传递相关的成员资格列表中,在计算 ECSD 时在传递关系列表中可以考虑的唯一成员资格是 MembershipDate 早于当前 MembershipID 的成员资格。例如根据给定 MembershipID 的 MembershipDates 和 Transfers table,如果查看 MembershipID 17,则在计算 MembershiID 17 的 ECSD 时不能考虑 MembershipID 19,因为 MembershipID 19 具有MembershipDate (Apr-08-2016) 不早于 MembershipID 17 (Dec-06-2011)
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 列 table。第一次忘记包括在内。
谢谢!
我认为这是准确的结果...虽然它与您的预期不同。请参阅我上面的评论。
由于第一个 table 是 CTE,您可以将其保留为 CTE 并继续使用此 CTE,或者将这些结果存储到临时 table 中,这可能是有益的,因为我们将查询它多次。
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)
或者,您可以在线编写此内容并一起跳过 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)
运行 对这个查询有点头疼,一些 hints/suggestions 将不胜感激。我找不到任何与我的问题真正相关的东西——我发现了一些关于传递闭包的东西,这不是我所需要的,因为我的数据可能会创建一个 loop/cycle,我认为这会导致递归调用无限循环。
假设我有两个基本 table,数据显示在它们下方。全面披露:Memberships table 一个 CTE 已经做了相当多的逻辑来计算 CServiceDate 列值。 Transfers table 是一个实际的 table,除了 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 | +-----+------------------+----------------+
问题 我需要查询的是 Memberships CTE 中的每一行(即每个 MembershipID),我想 return 所有相关 MembershipID 的 MIN CServiceDate。我将此最小值称为 ECSD(预期信用服务日期)。 ECSD的计算只有两个条件:
- 如果会员记录通过 Transfers table 通过查看FromMembershipID 和 ToMembershipID 列。例如对于 MembershipID 20,如果我们查看 Transfers table,我们可以看到 MembershipID 20、19、18 和 17 都是通过传递性相关的(此外:通过 Transfers table,我们可以看到MembershipIDs 15和16相互关联,但与[20,19,18,17] ) 无关
- 在上面直接派生的传递相关的成员资格列表中,在计算 ECSD 时在传递关系列表中可以考虑的唯一成员资格是 MembershipDate 早于当前 MembershipID 的成员资格。例如根据给定 MembershipID 的 MembershipDates 和 Transfers table,如果查看 MembershipID 17,则在计算 MembershiID 17 的 ECSD 时不能考虑 MembershipID 19,因为 MembershipID 19 具有MembershipDate (Apr-08-2016) 不早于 MembershipID 17 (Dec-06-2011)
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 列 table。第一次忘记包括在内。
谢谢!
我认为这是准确的结果...虽然它与您的预期不同。请参阅我上面的评论。
由于第一个 table 是 CTE,您可以将其保留为 CTE 并继续使用此 CTE,或者将这些结果存储到临时 table 中,这可能是有益的,因为我们将查询它多次。
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)
或者,您可以在线编写此内容并一起跳过 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)