【问题标题】:Replace 'while' with set-based operation for where用基于集合的操作替换 'while'
【发布时间】:2016-06-08 12:08:25
【问题描述】:

我使用的是 SQL Server 2012。我有一个查询,它将查看给定月份并通过检查该成员在给定月份的注册跨度来确定哪些成员在该月份处于活动/有效状态。查询如下所示:

select monthstart, monthend
into #tmp 
from monthtable --This table contains the month start and end dates I need

create table #result(ID varchar(20), monthstart varchar(8))

declare @eff varchar(8)
declare @end varchar(8)

while (select count(*) from #tmp) > 0
begin

select top 1 @eff = monthstart from #tmp order by monthstart
select top 1 @end = monthend from #tmp order by monthstart

insert into #result
select ID, @eff
from members
where ymdeff <= @end and ymdend >= @eff -- Ensures the member is active for at least one day in this month

end

select * from #result

我正在寻找一组大约 50 名成员,为期大约 8 个月。自从我开始写这篇文章以来,这个查询就一直在运行,而且还在继续。我认为它最终会得到正确的答案,但它需要的时间太长了。这似乎应该很简单,但我想不出任何其他方法来做到这一点,除了写出每个月的 where 语句,或者使用仍然是迭代而不是基于集合的动态 sql。有什么方法可以让我更快地完成这项工作?

感谢您的帮助!

【问题讨论】:

  • 我认为这个循环是无限的......你没有从#tmp删除数据
  • 像下面select m.ID, t.eff from members m , #tmp t where m.ymdeff &lt;= t.monthstart and m.ymdend &gt;= t.monthend这样创建连接怎么样
  • 为什么不在这里忘记循环并将其作为单个插入语句执行?

标签: sql sql-server while-loop


【解决方案1】:

当问题包含样本数据和预期输出时,回答这些问题会更容易。因此,如果我误解了您的架构,请接受我的歉意。

查询

SELECT
    m.ID,
    mt.MonthStart
FROM
    MonthTable mt
        INNER JOIN Members m        ON m.ymdeff BETWEEN mt.MonthStart AND mt.MonthEnd
GROUP BY
    m.ID,
    mt.MonthStart
;

此查询将 MonthTable 直接连接到 members 表。

【讨论】:

    【解决方案2】:

    您创建的循环是无限的。您需要在每次迭代时从 #tmp 中删除数据。

    while (select count(*) from #tmp) > 0
    begin
    select top 1 @eff = monthstart from #tmp order by monthstart
    select top 1 @end = monthend from #tmp order by monthstart
    
    insert into #result
    select ID, @eff
    from members
    where ymdeff <= @end and ymdend >= @eff -- Ensures the member is active  
    for at least one day in this month
    
    delete #tmp where monthstart=@eff and monthend =@end
    
    end
    

    您可以使用以下连接来代替循环

    select m.ID, t.monthstart from members m , #tmp t where m.ymdeff <= t.monthstart  and m.ymdend >= t.monthend
    

    【讨论】:

      【解决方案3】:

      正如@Ajay 所说,您在循环时忘记了临时表中的DELETE 行,因此您陷入了无限循环。

      为了完全避免循环(在 SQL 中几乎总是应该这样做):

      SELECT
          M.id,
          T.monthstart
      FROM
          #tmp T
      INNER JOIN Members M ON M.ymdeff <= T.monthend AND M.ymdend >= T.monthend
      

      我希望ymdeff 不是作为VARCHAR 坐在数据库中的日期...

      【讨论】:

      • 谢谢。有什么理由我不应该使用 varchar(8) 来存储日期?这就是源数据的来源。
      • 因为数据是日期,而不是字符串。如果您将其存储在varchar 列中,那么在某个时间点,您最终会得到一个“bob”日期,并且您的系统会因此而窒息。或者有人会以 d/m/y 而不是 y/m/d 的格式输入日期,现在你所有的比较操作都会突然失败。其他原因的列表比 cmets 的列表要长得多。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-06
      • 2021-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多