SQL Server 成员资格日期范围
Posted
技术标签:
【中文标题】SQL Server 成员资格日期范围【英文标题】:SQL Server Membership Date Range 【发布时间】:2013-04-04 14:02:38 【问题描述】:我是 SQL Server 的新手,有一个相当复杂的 SQL 任务,到目前为止我发现的解决方案都不适合我下面的问题。
我有一个数据库表,其中当前包含会员加入信息(摘录如下),其中包含会员支付会员费的每一年/期间的一行;会员可以取消一年/期间的会员资格并在以后重新加入。我们需要将每个连续成员表示为一行,这样下面的提取 1 的内容就变成了提取 2:
提取 1
Member_No Start_Date End_Date
--------- ---------- --------
10 2010-01-01 00:00:00.000 2011-01-01 00:00:00.000
10 2011-01-01 00:00:00.000 2012-01-01 00:00:00.000
10 2012-01-01 00:00:00.000 2013-01-01 00:00:00.000
10 2013-01-01 00:00:00.000 2014-01-01 00:00:00.000
20 2005-01-01 00:00:00.000 2006-01-01 00:00:00.000
20 2006-01-01 00:00:00.000 2007-01-01 00:00:00.000
20 2007-01-01 00:00:00.000 2008-01-01 00:00:00.000
30 2005-01-01 00:00:00.000 2006-01-01 00:00:00.000
30 2006-01-01 00:00:00.000 2007-01-01 00:00:00.000
30 2007-01-01 00:00:00.000 2008-01-01 00:00:00.000
30 2008-01-01 00:00:00.000 2009-01-01 00:00:00.000
30 2009-01-01 00:00:00.000 2010-01-01 00:00:00.000
30 2010-10-13 00:00:00.000 2011-01-01 00:00:00.000
30 2011-01-01 00:00:00.000 2012-01-01 00:00:00.000
30 2012-01-01 00:00:00.000 2013-01-01 00:00:00.000
我需要用下表替换上表的内容 - 有问题的表中有更多记录,我将非常感谢任何人可以提供的任何助手:
提取 2
Member_No Start_Date End_Date
--------- ---------- --------
10 2010-01-01 00:00:00.000 2014-01-01 00:00:00.000
20 2005-01-01 00:00:00.000 2008-01-01 00:00:00.000
30 2005-01-01 00:00:00.000 2010-01-01 00:00:00.000
30 2010-10-13 00:00:00.000 2013-01-01 00:00:00.000
【问题讨论】:
【参考方案1】:这是一种更好的方法(原始方法仍在下方):
with ms as (
select ms.*,
(select 1 from Membership ms2 where ms2.Member_no = ms.Member_no and cast(ms2.Start_date as date) = cast(ms.End_date as date)
) as LinkedToNext
from MemberShip ms
)
select member_no, MIN(start_date) as start_date, MAX(end_date) as end_date
from (select ms.*,
(select top 1 end_date
from ms ms2 where ms2.Member_no = ms.Member_No and ms2.LinkedToNext is NULL and ms2.Start_Date >= ms.Start_Date
order by end_date desc
) as grouping
from ms
) ms1
group by Member_no, grouping
CTE 确定某项是否与下一项相关联。然后,分组是第一条记录的结束日期,之后不链接到链接链的下一条记录,这始终相同。
回应您关于“不止一行”的评论。这意味着一个成员有两个从同一日期开始的成员资格。如果这是唯一的重叠形式,您可以将其修复为:
with ms as (
select member_no, start_date, max(end_date) as end_date
(select 1 from Membership ms2 where ms2.Member_no = ms.Member_no and cast(ms2.Start_date as date) = cast(ms.End_date as date)
) as LinkedToNext
from MemberShip ms
group by member_no, start_date
)
比我原来的方法简单得多,我仍然保留在下面:
with ms as (
select ms.*,
(select 1 from Membership ms2 where ms2.Member_no = ms.Member_no and ms2.Start_date = ms.End_date
) as LinkedToNext,
(select ms2.End_Date from Membership ms2 where ms2.Member_no = ms.Member_no and ms2.Start_date = ms.End_date
) as NextEndDate,
(select 1 from Membership ms2 where ms2.Member_no = ms.Member_no and ms2.End_date = ms.Start_date
) as LinkedToPrev
from MemberShip ms
)
select member_no, MIN(start_date) as start_date,
MAX(coalesce(NextEndDate, End_Date)) as end_date
from (select ms.*,
(ROW_NUMBER() over (partition by Member_no order by Start_Date) -
ROW_NUMBER() over (partition by Member_no, LinkedToNext order by Start_date)
) as grouping
from ms
) ms1
where not (LinkedToNext is null and LinkedToPrev = 1)
group by member_no, grouping
这使用相关子查询来确定一个成员资格是否与下一个成员相关联(基于开始日期与结束日期相同——如果您愿意,您实际上可以添加一个捏造因素)。
然后它使用了一个技巧。它按 start_date 为每个成员枚举行。它还根据每个成员是否链接到下一个成员,按开始日期枚举行。对于可以组合在一起的链,差异是恒定的。
最后一步是将其分组以获得最终结果。
一个复杂的问题是获得正确的结束日期。初始代码在计算中不包括链的末端。所以,我从下一条记录中借用了结束日期。然后过滤掉这条结束记录。
【讨论】:
嗨,戈登,感谢您的回复。第一篇文章的结果更接近我想要实现的目标;但是,它包含额外的行 - 您能否帮助更新 SQL 以删除第 2、4、7 和 8 行? (根据我在下面的原始帖子中提供的数据运行您的 sql 的结果)member_no start_date end_date 10 2010-01-01 2014-01-01 10 2013-01-01 2014-01-01 20 2005-01-01 2008-01-01 20 2007-01-01 2008-01-01 30 2005-01-01 2010-01-01 30 2010-10-13 2013-01-01 30 2009-01-01 2010-01-01 30 2012-01-01 2013-01-01
@Babs 。 . .我的错。我在第二个相关子查询中遗漏了order by
。
很抱歉让您感到痛苦-“更好的方法”确实效果更好。但是,当我对整个数据库/超过 925 行数据运行查询时,出现以下错误:Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
上面的错误似乎是由于未在解决方案中使用“JOIN”。我尝试解决这个问题无济于事 - 你能帮忙吗?
@Babs 。 . .这意味着您对给定成员具有重叠的成员资格。您可能想就该情况提出另一个问题。它使问题与您当前的问题大不相同(除非我的答案中的简单修复就足够了)。
最新的解决方案工作正常,但是当我引入一个额外的列时,它没有。您能否帮助我需要进行哪些更改以允许结果也按例如分组?会员类型/结果如下?以上是关于SQL Server 成员资格日期范围的主要内容,如果未能解决你的问题,请参考以下文章
使用 SQL Server 2005 通过 LDAP 访问 Active Directory 角色成员资格
如何使 SQL Server 数据库在附加用户和角色成员资格时继承它们?
部署到 Azure 应用服务时,Asp.net 成员资格提供程序无法连接到本地 SQL Server