基于集合的SQL Server循环替代方法

时间:2012-05-22 18:32:02

标签: sql sql-server sql-server-2005 loops

我知道有几篇关于尝试在存储过程中在SQL Server中循环的BAD的帖子。但我还没有找到我想要做的事情。我们正在使用可以在内部直接链接到Excel的数据连接。

我看过一些帖子,其中有些人说他们可以将大多数循环转换为标准查询。但是对于我的生活,我遇到了这个问题。

我需要所有在{38,40类型事件发生之前订单的custID人。但只有在事件和第一个查询中的订单之间没有其他顺序时才能获取它们。

所以有3个部分。我首先根据时间框架查询所有订单(订单表)到临时表中。

Select into temp1 odate, custId from orders where odate>'5/1/12'

然后,我可以使用临时表在辅助表上进行内部联接,以获取可能在当前订单之前的某个时间发生的客户事件(LogEvent表)。

Select into temp2 eventdate, temp1.custID from LogEvent inner join temp1 on 
temp1.custID=LogEvent.custID where EventType in (38,40) and temp1.odate>eventdate
order by eventdate desc

这里的问题是我尝试运行的查询将从第一个查询返回每个客户的所有行,其中我只想要每个客户的最新版本。所以这就是在客户端我将循环到只获得一个事件而不是所有旧事件。但由于所有查询都必须在Excel内部运行,因此我无法真正循环客户端。

然后,第三步可以使用第二个查询的结果来检查事件是否发生在最新订单和任何先前订单之间。我只想要事件在订单之前的数据,而且之间没有其他订单。

Select ordernum, shopcart.custID from shopcart right outer join temp2 on 
shopcart.custID=temp2.custID where shopcart.odate >= temp2.eventdate and
ordernum is null

有没有办法简化这个并使它基于集合在SQL Server中运行而不是我在客户端执行的某种循环?

2 个答案:

答案 0 :(得分:1)

这是切换到基于集合的表示法的一个很好的例子。

首先,我将所有三个查询合并为一个查询。通常,使用单个查询让查询优化器执行它最擅长的操作 - 确定执行路径。它还可以防止在多线程/多处理器计算机上意外序列化查询。

键是row_number(),用于对事件进行排序,因此最新的事件的值为1.您将在最终的WHERE子句中看到这一点。

select ordernum, shopcart.custID
from (Select eventdate, temp1.custID,
             row_number() over (partition by temp1.CustID order by EventDate desc) as seqnum
      from LogEvent inner join
           (Select odate, custId
            from order
            where odate>'5/1/12'
           ) temp1 
           on temp1.custID=LogEvent.custID
      where EventType in (38,40) and temp1.odate>eventdate order by eventdate desc 
     ) temp2 left outer join
     ShopCart
     on shopcart.custID=temp2.custID
 where seqnum = 1 and shopcart.odate >= temp2.eventdate and ordernum is null

我保留了您的命名约定,即使我认为“from order”会产生语法错误。即使不是这样,使用保留的SQL单词命名表和列也是不好的做法。

答案 1 :(得分:0)

如果您使用的是较新版本的sql server,则可以使用ROW_NUMBER功能。我将很快写一个例子。

;WITH myCTE AS
( 
SELECT
    eventdate, temp1.custID, 
    ROW_NUMBER() OVER (PARTITION BY temp1.custID ORDER BY eventdate desc) AS CustomerRanking 
FROM LogEvent 
JOIN temp1 
    ON temp1.custID=LogEvent.custID 
WHERE EventType IN (38,40) AND temp1.odate>eventdate
)
SELECT * into temp2 from myCTE WHERE CustomerRanking = 1;

这为您提供了没有循环的每个客户的最新活动。

此外,您可以使用RANK,但这会为关系创建重复项,而ROW_NUMBER将保证您的分区没有重复的数字。

相关问题