使用SQL 2005/2008
我必须使用前进光标,但我不想遭受糟糕的表现。有没有更快的方法可以循环而不使用游标?
答案 0 :(得分:4)
答案 1 :(得分:4)
您可以执行WHILE
循环,但是您应该尝试实现更多基于集合的操作,因为SQL中的任何迭代都会遇到性能问题。
答案 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