我有一个非常大的脚本,它使用游标和嵌套游标。
我遇到了性能问题,我发现脚本中完成main while循环的最后一条指令占用了大部分时间:
SET STATISTICS TIME ON
FETCH NEXT FROM OldMetaOffer_cursor
INTO @MetaOfferId, @CustomerId, @OfferName, @CheckedOutById, @CheckOutDate, @LastOfferStatusId, @LastCalculationNumber, @CreatedByDisplayName, @CreatedById, @CreateDate, @CoordinatorId, @CoordinatorDate, @CentralAnalystId, @CentralAnalystDate, @DeployUserId, @DeploymentDate, @OwnerId;
SET STATISTICS TIME OFF
需要4秒以上,而一步完成需要4,6秒
表MetaOffer有大约150 k行但我在8,5 k行上使用游标。 (我在开头过滤行。)
有没有办法改善这种糟糕的表现?
在循环开始时我有:
DECLARE @MetaOfferId uniqueidentifier
, @MetaOfferTypeId int
, @CustomerId uniqueidentifier -- CustomerId
, @OfferName nvarchar(50) -- OfferName
, @CheckedOutById int -- CheckOutById
, @CheckOutDate datetime -- CheckOutDate
, @LastOfferStatusId int -- LastProcessStatusId
, @LastCalculationNumber nvarchar(20) -- LastCalculationNumber
, @CreatedByDisplayName nvarchar(300) -- CreatedByDisplayName
, @CreatedById int -- CreatedById
, @CreateDate datetime -- CreateDate
, @CoordinatorId int -- CoordinatorId
, @CoordinatorDate datetime -- CoordinatorDate
, @CentralAnalystId int -- CentralAnalystId
, @CentralAnalystDate datetime -- CentralAnalystDate
, @DeployUserId int -- DeployUserId
, @DeploymentDate datetime -- DeploymentDate
, @OwnerId int -- OwnerId
-- id statusu po zmapowaniu
, @NewLastOfferStatusId int
DECLARE OldMetaOffer_cursor CURSOR FOR
SELECT MetaOfferId, CustomerId, OfferName, CheckedOutById, CheckOutDate, LastOfferStatusId, LastCalculationNumber, CreatedByDisplayName, CreatedById,
CreateDate, CoordinatorId, CoordinatorDate, CentralAnalystId, CentralAnalystDate, DeployUserId, DeploymentDate, OwnerId
FROM [Other].[dbo].[MetaOffer] MO where
exists
(select * from [Other].[dbo].[OfferHistoryItem]
where MetaOfferId = MO.MetaOfferId and NewStatusId = 9 and DiscountId is null and KoosOfferId is null)
也许有一个问题,在下一次获取这个查询再次进行?结果在任何地方都没有缓冲。如果是这样,我可以通过任何方式获得该查询的结果并对数据进行操作而不对每个循环步骤进行查询吗?
答案 0 :(得分:5)
由于你遗漏了问题中最重要的部分(游标实际上做了什么),我只是给你这个参考,希望能告诉你如何在没有游标的情况下完成你的任务。游标表现极差,如果存在任何其他替代方案,则不应使用游标。我曾经通过移除光标将过程从45分钟更改为不到1分钟,另一个过程从24小时变为大约30分钟。使用游标的原因很少,很多不使用游标。它们是最后的技术,而不是你尝试的第一件事。
http://wiki.lessthandot.com/index.php/Cursors_and_How_to_Avoid_Them
答案 1 :(得分:2)
如果您查看任何合理当前版本的SQL Server的CURSOR文档,我也会考虑其他几个RDBMS,您会发现它不会使用CURSOR和它们被包括在内的exaplanation仅向后兼容。
有许多方法可以避免它们,但它确实取决于您正在使用的数据库服务器。
例如,您可以使用临时表:
SELECT
[ID], [col1], [col2]
INTO
#stuff
WHERE
[col1] LIKE '%something%'
DECLARE @id int, @c1 varchar(32), @c2 varchar(32)
SELECT TOP 1 @id = [ID], @c1 = [col1], @c2 = [col2]
FROM #stuff
WHILE @@ROWCOUNT > 0
BEGIN
-- do something, say execute a stored procedure, for each row
EXEC someproc @id, @c1, @c2
SELECT TOP 1 @id = [ID], @c1 = [col1], @c2 = [col2]
FROM #stuff
WHERE [ID] > @id
END
根据代码的作用以及您正在使用的数据库,可能会有更高效的解决方案,但临时表无论如何都会提供相对于游标的大量改进。
如果在SQL Server上,请注意如果您编写访问表的标量函数(实际上执行SELECT语句或调用其他函数),这样的函数实际上会成为游标 - 所以尽可能避免使用它。
答案 2 :(得分:0)
游标很慢。嵌套游标甚至更慢。除此之外,我们需要查看更多具体信息以提供任何有用的建议。