什么是sql循环游标的替代方法?

时间:2011-03-24 21:17:07

标签: sql sql-server-2005 tsql sql-server-2008

使用SQL 2005/2008

我必须使用前进光标,但我不想遭受糟糕的表现。有没有更快的方法可以循环而不使用游标?

9 个答案:

答案 0 :(得分:4)

答案 1 :(得分:4)

您可以执行WHILE循环,但是您应该尝试实现更多基于集合的操作,因为SQL中的任何迭代都会遇到性能问题。

http://msdn.microsoft.com/en-us/library/ms178642.aspx

答案 2 :(得分:3)

公共表格表达式将是@Neil建议的一个很好的选择。以下是Adventureworks的一个例子:

WITH cte_PO AS 
(
SELECT [LineTotal]
  ,[ModifiedDate]
FROM [AdventureWorks].[Purchasing].[PurchaseOrderDetail]
),
minmax AS
(
    SELECT MIN([LineTotal]) as DayMin
        ,MAX([LineTotal]) as DayMax
        ,[ModifiedDate]
    FROM cte_PO
    GROUP BY [ModifiedDate]
)
SELECT * FROM minmax ORDER BY ModifiedDate

以下是它返回的前几行:

DayMin     DayMax     ModifiedDate
135.36     8847.30    2001-05-24 00:00:00.000
129.8115   25334.925  2001-06-07 00:00:00.000

答案 3 :(得分:2)

  

我必须使用前进光标,但我不想遭受糟糕的表现。有没有更快的方法可以循环而不使用游标?

这取决于你对光标的处理方式。

几乎所有东西都可以使用基于集合的操作重写,在这种情况下,循环在查询计划中执行,因为它们不涉及上下文切换的速度要快得多。

然而,有些事情SQL Server并不擅长,比如计算累积值或加入日期范围。

使用CURSOR

可以更快地完成这些类型的查询

但同样,这是一个非常罕见的例外,通常基于集合的方式表现得更好。

如果您发布了查询,我们可能会对其进行优化并删除CURSOR

答案 4 :(得分:1)

说“游标影响SQL的性能”是不准确的。他们当然有倾向,但很多都与人们如何使用它们有关。

第一种选择是尝试找到解决问题的基于集合的方法。

如果逻辑上没有基于集合的方法,并且Cursor的查询正在访问真实(非临时)表,那么使用STATIC关键字将SELECT语句的结果放入临时表中,因此将在迭代结果时不要锁定查询的基表。如果您需要对处理结果集时可能消失的记录保持敏感,那么使用STATIC将无济于事,但如果您正在考虑针对Temp Table转换为WHILE循环,那么这是一个没有实际意义的点(因为这也不知道更改基础数据)。

答案 5 :(得分:1)

根据您的需要,您可以使用计数表。

杰夫·莫登(Jeff Moden)有一篇关于理货榜Here

的精彩文章

答案 6 :(得分:1)

不要使用游标,而是寻找基于集合的解决方案。如果找不到基于集合的解决方案......仍然不要使用游标!发布您想要实现的细节,有人将能够为您找到基于集合的解决方案。

答案 7 :(得分:1)

以下是使用cursor的示例:

DECLARE @VisitorID int
DECLARE @FirstName varchar(30), @LastName varchar(30)
-- declare cursor called ActiveVisitorCursor 
DECLARE ActiveVisitorCursor Cursor FOR
SELECT VisitorID, FirstName, LastName 
FROM Visitors
WHERE Active = 1
-- Open the cursor
OPEN ActiveVisitorCursor 
-- Fetch the first row of the cursor and assign its values into variables
FETCH NEXT FROM ActiveVisitorCursor INTO @VisitorID, @FirstName, @LastName 
-- perform action whilst a row was found
WHILE @@FETCH_STATUS = 0
BEGIN
 Exec MyCallingStoredProc @VisitorID, @Forename, @Surname
 -- get next row of cursor
 FETCH NEXT FROM ActiveVisitorCursor INTO @VisitorID, @FirstName, @LastName 
END
 -- Close the cursor to release locks
CLOSE ActiveVisitorCursor 
 -- Free memory used by cursor
DEALLOCATE ActiveVisitorCursor 

现在这里是一个例子,我们如何在不使用游标的情况下获得相同的结果:

/* Here is alternative approach */

-- Create a temporary table, note the IDENTITY
-- column that will be used to loop through
-- the rows of this table
CREATE TABLE #ActiveVisitors (
       RowID int IDENTITY(1, 1), 
       VisitorID int,
       FirstName varchar(30),
       LastName varchar(30)
 )
DECLARE @NumberRecords int, @RowCounter int
DECLARE @VisitorID int, @FirstName varchar(30), @LastName varchar(30)

-- Insert the resultset we want to loop through
-- into the temporary table
INSERT INTO #ActiveVisitors (VisitorID, FirstName, LastName)
SELECT VisitorID, FirstName, LastName
FROM Visitors
WHERE Active = 1 

-- Get the number of records in the temporary table
SET @NumberRecords = @@RowCount 
--You can use: SET @NumberRecords = SELECT COUNT(*) FROM #ActiveVisitors
SET @RowCounter = 1

-- loop through all records in the temporary table
-- using the WHILE loop construct
WHILE @RowCounter <= @NumberRecords
BEGIN
 SELECT @VisitorID = VisitorID, @FirstName = FirstName, @LastName = LastName 
 FROM #ActiveVisitors
 WHERE RowID = @RowCounter

 EXEC MyCallingStoredProc @VisitorID, @FirstName, @LastName

 SET @RowCounter = @RowCounter + 1
END

-- drop the temporary table
DROP TABLE #ActiveVisitors

答案 8 :(得分:0)

在某些情况下,可以使用Tally tables。它可能是循环和cusrors的一个很好的替代品,但请记住它不能在每种情况下应用。可以找到一个很好的解释案例here