循环访问SQL查询结果并与表值进行比较?

时间:2017-05-30 20:08:28

标签: sql sql-server tsql

我有一个表(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范围。

在存储过程或查询中显示此任何帮助将非常感激和有帮助。谢谢你的时间!

4 个答案:

答案 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
      )

注意:

测试设置:

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'