1

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

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

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

所以有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 中运行,而不是我在客户端执行的某种循环?

4

2 回答 2

2

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

首先,我将您的所有三个查询组合成一个查询。一般来说,只有一个查询让查询优化器做它最擅长的事情——确定执行路径。它还可以防止在多线程/多处理器机器上意外序列化查询。

关键是 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

我保留了您的命名约定,即使我认为“从顺序”应该会产生语法错误。即使不是这样,用保留的 SQL 字命名表和列也是不好的做法。

于 2012-05-22T18:46:30.747 回答
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 将保证您的分区没有重复的数字。

于 2012-05-22T18:35:22.233 回答