我有一个表(Client_Programs),其中包含客户列表和他们注册的政府计划。每个客户都由唯一的ClientULink识别,程序有ProgramULink。每个条目都包含一个StartDate和EndDate。所以数据可能如下所示:
ClientULink ProgramULink StartDate EndDate
b0afd171-28d0-48a3-989f-f2b0583c0fxx 46dcdf26-4916-4966-ac81-eaf57c7a26xx 2/2/2017 6:00:00 AM 5/2/2017 6:00:00 AM
对于相同(或不同)的程序,客户端可以多次在此表中。我被要求创建一个未重新注册到程序中的客户端的查询(报告)。我的想法是最初通过运行查询来从Client_Program表中返回两个EndDates之间的条目(这将是我在该报告的最终存储过程中的输入参数)。例如:
SELECT ClientULink, ProgramULink, StartDate, EndDate FROM Client_Program cp WHERE cast(cp.EndDate as date) BETWEEN '2017-05-01' AND '2017-05-30'
但是,我想将它们放入临时表或内存中,以便我可以循环遍历它们并检查所有这些结果对主Client_Program表,看看这些客户端是否有任何"更新&#34 ;显示他们是否重新注册的条目。
SELECT ClientULink, ProgramULink, StartDate, EndDate FROM Client_Program cp WHERE ClientULink = 'b0afd171-28d0-48a3-989f-f2b0583c0fxx' AND cp.ProgramULink = '46dcdf26-4916-4966-ac81-eaf57c7a26xx' AND CAST(cp.StartDate as DATE) > '2017-05-30'
我的问题是如何循环我的第一个结果查询?基本上,我试图找到任何未重新注册的客户端(没有为程序提供更新的StartDate条目 - 或者我的查询中显示的特定程序),而不是指定的EndDate范围。
在存储过程或查询中显示此任何帮助将非常感激和有帮助。谢谢你的时间!
答案 0 :(得分:4)
您可以使用cursor
来执行循环,但与基于集合的操作相比,这是一个表现不佳的选项。
使用not exists()
返回5月结束的程序中尚未重新注册的所有客户端:
select
ClientULink
, ProgramULink
, StartDate
, EndDate
from Client_Program cp
where cp.EndDate >= '20170501'
and cp.EndDate < '20170601'
and not exists (
select 1
from Client_Program i
where i.ClientULink = cp.ClientULink
and i.ProgramULink = cp.ProgramULink
and i.StartDate >= cp.EndDate
)
注意:
between
and the devil have in common? - Aaron Bertrand not in()
, outer apply()
, left outer join
, except
, or not exists()
? - Aaron Bertrand datetime
和smalldatetime
,格式为:YYYYMMDD
和YYYY-MM-DDThh:mm:ss[.nnn]
- {{3} } 测试设置:
create table Client_Program (ClientULink uniqueidentifier, ProgramULink uniqueidentifier, StartDate datetime, EndDate datetime)
insert into Client_Program values
('00000000-0000-0000-0000-000000000000','00000000-0000-0000-0000-000000000000','2017-02-02T06:00:00','2017-05-02T06:00:00')
,('11111111-1111-1111-1111-111111111111','11111111-1111-1111-1111-111111111111','2017-02-02T06:00:00','2017-05-02T06:00:00')
,('11111111-1111-1111-1111-111111111111','11111111-1111-1111-1111-111111111111','2017-05-02T06:00:00','2017-08-02T06:00:00')
select
ClientULink
, ProgramULink
, StartDate = convert(char(10),StartDate,120)
, EndDate = convert(char(10),EndDate,120)
from Client_Program cp
where cp.EndDate >= '20170501'
and cp.EndDate < '20170601'
and not exists (
select 1
from Client_Program i
where i.ClientULink = cp.ClientULink
and i.ProgramULink = cp.ProgramULink
and i.StartDate >= cp.EndDate
)
rextester演示:Bad habits to kick : mis-handling date / range queries - Aaron Bertrand
返回:
+--------------------------------------+--------------------------------------+------------+------------+
| ClientULink | ProgramULink | StartDate | EndDate |
+--------------------------------------+--------------------------------------+------------+------------+
| 00000000-0000-0000-0000-000000000000 | 00000000-0000-0000-0000-000000000000 | 2017-02-02 | 2017-05-02 |
+--------------------------------------+--------------------------------------+------------+------------+
您还可以在having
子句中使用聚合和过滤器max(EndDate)
在某个日期范围之间(如果他们重新注册,那么他们的max(EndDate)
将不在日期范围内):
select
ClientULink
, ProgramULink
from Client_Program cp
group by
ClientULink
, ProgramULink
having max(EndDate)>= '20170501'
and max(EndDate) < '20170601'
返回:
+--------------------------------------+--------------------------------------+
| ClientULink | ProgramULink |
+--------------------------------------+--------------------------------------+
| 00000000-0000-0000-0000-000000000000 | 00000000-0000-0000-0000-000000000000 |
+--------------------------------------+--------------------------------------+
答案 1 :(得分:2)
通常,如果您在SQL中循环,那么可能做错了。您可以使用EXCEPT
来实现此目的。
SELECT ClientULink
FROM Client_Program
WHERE CAST(EndDate as date) BETWEEN '2017-05-01' AND '2017-05-30'
EXCEPT
SELECT ClientULink
FROM Client_Program
WHERE CAST(StartDate as date) > '2017-05-01'
修改:如果客户在DateRange中开始和结束,则会错误地将其排除在结果之外。
答案 2 :(得分:1)
你确定你没有过度思考这个吗?未重新注册似乎是计数为1的ID(客户端,程序)集。例如,
select cp.ClientULink, cp.ProgramULink from dbo.Client_Program as cp
group by cp.ClientULink, cp.ProgramULink
having count(*) = 1
order by cp.ClientULink, cp.ProgramULink ;
你还需要什么?
答案 3 :(得分:0)
如果您需要日期过滤器,请尝试以下操作:
select ClientULink, ProgramULink, StartDate, EndDate
from Client_Program cp
inner join
(
select ClientULink, ProgramULink, count(*) as Count
from Client_Program cp2
group by ClientULink, ProgramULink
having count(*) = 1
) cp2
on cp.ClientULink = cp2.ClientULink
and cp.ProgramULink = cp2.ProgramULink
where cast(cp.EndDate as date) between '2017-05-01' and '2017-05-30'