Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/86.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
SQL Server中基于集合的循环替代方案_Sql_Sql Server_Sql Server 2005_Loops - Fatal编程技术网

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

SQL Server中基于集合的循环替代方案,sql,sql-server,sql-server-2005,loops,Sql,Sql Server,Sql Server 2005,Loops,我知道有几篇文章谈到了在存储过程中尝试循环SQL Server有多糟糕。但我还没有完全找到我要做的事情。我们使用的数据连接可以在内部直接链接到excel 我看过一些帖子,其中一些人说他们可以将大多数循环转换为标准查询。但就我的一生而言,我在这件事上遇到了麻烦 我需要所有在38,40型事件之前有订单的custIDs。但只有在事件和第一个查询中的顺序之间没有其他顺序时,才能获取它们 所以有三个部分。我首先根据时间框架将所有订单(订单表)查询到一个临时表中 Select into temp1 odat

我知道有几篇文章谈到了在存储过程中尝试循环SQL Server有多糟糕。但我还没有完全找到我要做的事情。我们使用的数据连接可以在内部直接链接到excel

我看过一些帖子,其中一些人说他们可以将大多数循环转换为标准查询。但就我的一生而言,我在这件事上遇到了麻烦

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

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

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

如果您使用的是较新版本的SQL Server,则可以使用此函数。我将很快写一个例子

;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;
这将为每个客户获取最新的事件,而无需循环


此外,您还可以使用,但是,这将为领带创建重复的编号,而ROW_NUMBER将保证分区没有重复的编号。

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

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

键是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字命名表和列也是一种不好的做法。

我使用的是2005和2008。我们开始迁移到2008年,但还没有完成,所以我也需要在2005年解决这个问题。是5月1日还是1月5日?请使用安全、明确的日期文字格式,例如
'20120501'
。。。SQL Server永远不会误解这一点,这里的用户、同事或读者也不会误解这一点。不管是一月还是五月,这都无关紧要。日期与查询无关,因为它将被动态插入。但是谢谢你关于简洁的说明。你想要什么还不清楚。这个猜测正确吗?给定一个日期
@D
,返回
[custID]
,给每一个在
[odate]>@D
上有订单的客户1)一个订单
[ordernum]
,2)在
[eventdate]<[odate]
之前和之后没有订单
[eventdate]
。我看不出下面Gordon的查询如何满足要求3。从您的描述中不清楚的一点是:A)日期列是纯日期吗?如果客户在同一日期下了两个订单,您希望在该日期之前的事件38或40有任何结果吗?B) 如果[eventdate]位于@D之前的订单之前,该怎么办?实际返回值将只是在
@D
时间段内有订单的客户ID,该客户ID之前有类型为38或40的事件。因此,我排除了事件后可能是多个订单的额外订单。我喜欢这个想法,但在我的初始查询中,它必须遍历所有客户的整个事件表。这有可能成为数万条记录。循环可以为每个订单提供一条记录。@CaptainBli您需要用基于集合的逻辑,而不是过程逻辑来思考。这将限制您在集合中的查询。我已经修改了我的答案,在使用CTE的第一个查询中包含客户排名,其中=1。优化器应该处理其余的部分。在SQL中,当不必要时循环总是一个坏主意..正如您已经提到的:)@CaptainBli:在查看其查询计划之前,您不会知道此查询是否效率低下。由于子句
CustomerRanking=1
,有了支持索引,查询可能会使用段运算符和Top运算符来避免处理
LogEvent
的每一行。(从我对你的问题的评论中注意到,不管怎么说,我认为这个查询是错误的。我在这里的评论只是回应你的评论,即它“必须迭代整个事件表。”)是的,我在回答中想提到这一点,但整个查询肯定可以收紧,正如Steve Kass已经提到的,声明您的代码并在之后检查优化…不要过度思考优化器…这就是ORM的工作方式…大多数情况下您不需要调整任何东西:)是的,很抱歉应该是订单而不是订单。行号()将迭代给定客户的所有事件。这比循环和为每个客户请求单个事件行快吗?查询的客户组可能有10000个事件行。我正在努力更好地理解这一切。是的!数据库内部的“循环”比使用游标循环快得多。游标有很多开销,将每个值带回来并用于