需要SQL游标建议 - 检查一组日期范围内的唯一日期列表

时间:2017-03-27 05:52:31

标签: sql-server cursor

我在T-SQL中自学成才,如果我用一个简单的答案问一个问题,请原谅我,但我已经知道了我的知识,我的互联网搜索我没有教我正在寻找的东西......

我有两张桌子,我正在使用:

tbl_A (exit)
----------------------------
id_num | exit_dte
----------------------------
100001 | 2001-01-15
100002 | 2001-01-31
100003 | 2001-06-03

tbl_B (period)
---------------------------------------------
period_id | period_start_dte | period_end_dte
---------------------------------------------
        1 |       2001-01-01 |     2001-01-31
        2 |       2001-07-01 |     2001-07-31

系统界面允许数据用户将他们想要的任何日期放入tbl_A.exit_dte,尽管他们的(未经写入的)业务策略声明他们不应该在该列中放置一个不会下降的日期在tbl_B中的记录的period_start_dte和period_end_dte之间。我需要找到任何不在period_id的开头和结尾之间的exit_dte。

我的第一步是准备一份不同的现有tbl_A.exit_dte值列表。我知道怎么做:

Select distinct tbl_A.exit_dte from tbl_A

我不知道怎么做,但我想我想做的是使用游标循环遍历此列表的值,并为每个值确定它是否介于开始之间以tbl_B为期的结束日期。如果它在一个期间日期范围内,我将显示"在范围&#34 ;;否则,我会显示"错误" (我理解描述符是不相关的。)

2001-01-15 | In Range  (matches tbl_B.period_id = 1)
2001-01-31 | In Range  (matches tbl_B.period_id = 1) 
2001-06-03 | Error

以下是我为了获得理想结果所尝试的内容:

    Declare @ques_dte datetime
    Declare @Getid cursor

    Set @Getid = CURSOR FOR
    Select distinct exit_dte
    from tbl_A

    Open @Getid
    Fetch NEXT
    from @Getid into @ques_dte

    while @@FETCH_STATUS = 0

    Begin

        select
        @ques_dte as checked_date
        , case 
            when tbl_B.period_id is not null then 'In Range'
            else 'Error' end as Date_Period_Check
        from tbl_B
        where @ques_dte between tbl_B.period_start_dte and tbl_B.period_end_dte

        Fetch NEXT
        From @Getid INTO @ques_dte
    END

    Close @Getid
    Deallocate @Getid

因此,当我运行查询时,查询并不会爆炸(总是一个好兆头),但有几个问题。首先,SSMS中的结果使它看起来像是为每个@ques_dte值(可能是)运行一个不同的查询,而不是一组长的结果。我确定这是由于我对BEGIN,FETCH,END的CURSOR操作的不正确理解。

其次,查询显示@ques_dte 匹配一个tbl_B.period_id,并显示" In Range"值,那些不匹配的@ques_dte,不显示@ques_dte或"错误"值 - 它只是一个空记录集。我对此感到困惑。

第三,(这可能是因为我不耐烦而且不理解基于集合的SQL操作之间的区别,不管这是什么......)查询似乎需要很长时间才能运行。在我的生产环境中,tbl_B中有164条记录,而来自tbl_A的不同查询中有1011条记录。这些似乎是小数字,但查询需要29秒才能完成。

我欢迎专家的反馈和建议。我不太关心执行时间 - 我可以延迟生活 - 但如果有人愿意在我的前两个问题上教育我,我会非常感激。

2 个答案:

答案 0 :(得分:1)

对我而言,就像一个简单的左连接就可以了:

SELECT id_num, exit_dte, period_id
FROM [exit] 
LEFT JOIN period 
ON exit_dte > period_statt_dte
AND exit_dte < period_end_dte

您将获得范围内的日期范围的period_id,以及不包含范围的日期的null。

如果要显示“在范围内”和“错误”,可以使用大小写:

SELECT  exit_dte, 
        CASE WHEN period_id IS NOT NULL THEN 
            'In Range' 
        ELSE
            'Error' 
        END
FROM [exit] 
LEFT JOIN period 
ON exit_dte > period_statt_dte
AND exit_dte < period_end_dte

答案 1 :(得分:1)

这将是您所需要的,它将基于一组数据而不是在第二个表的每一行(如光标所做的那样)逐个进行:

select id_num, exit_dte 
from table1 t1 left join table2 t2 on t1.exit_dte between period_start_dte and period_end_dte
where t2.period_id is null