如何提高 SQL 服务器中 Cursor 的性能

How to improve the performance of a Cursor in SQL Server

我有一个 table tblLogins,其中保存了 20 万用户数据。

我需要在另一个 table 中为每个用户插入 30 条记录。我使用光标来完成这项任务。但是我写的脚本要花很多时间。

2小时仅插入6万用户数据

我查看了 google 的解决方案,但没有找到任何与提高性能相关的内容。

下面是我写的脚本

DECLARE @LoginID int
DECLARE @DomainID int

DECLARE curDomain CURSOR FAST_FORWARD 
FOR SELECT tbldomains_id FROM  tblDomains

OPEN curDomain 

FETCH NEXT FROM curDomain INTO @DomainID

WHILE @@FETCH_STATUS = 0

BEGIN
 --cur2 starts

 DECLARE curLogin CURSOR FAST_FORWARD 
 FOR SELECT tbllogins_id FROM  tbllogins where tbldomains_id = @DomainID

 OPEN curLogin 

 FETCH NEXT FROM curLogin INTO @LoginID

 WHILE @@FETCH_STATUS = 0

 BEGIN

 --code starts

 if not exists(select 1 from tblWidgetProperties where tblLogin_id = @LoginID)
 begin
 Insert tblWidgetProperties values(1,@LoginID,'isEnabled','True')
 Insert tblWidgetProperties values(2,@LoginID,'isEnabled','True')
 Insert tblWidgetProperties values(3,@LoginID,'isEnabled','True')
 Insert tblWidgetProperties values(4,@LoginID,'isEnabled','True')
 Insert tblWidgetProperties values(5,@LoginID,'isEnabled','True')
 Insert tblWidgetProperties values(6,@LoginID,'isEnabled','True')
 Insert tblWidgetProperties values(7,@LoginID,'isEnabled','True')
 Insert tblWidgetProperties values(8,@LoginID,'isEnabled','True')
 Insert tblWidgetProperties values(9,@LoginID,'isEnabled','True')
 Insert tblWidgetProperties values(10,@LoginID,'isEnabled','True')
 Insert tblWidgetProperties values(11,@LoginID,'isEnabled','True')
 end

 if not exists(select 1 from tblWidgetPosition where tblLogins_id = @LoginID)
 begin
 Insert tblWidgetPosition values(3,1.0,@LoginID)
 Insert tblWidgetPosition values(4,1.01,@LoginID)
 Insert tblWidgetPosition values(5,1.02,@LoginID)
 Insert tblWidgetPosition values(11,1.03,@LoginID)
 Insert tblWidgetPosition values(1,2.00,@LoginID)
 Insert tblWidgetPosition values(7,2.01,@LoginID)
 Insert tblWidgetPosition values(9,2.02,@LoginID)
 Insert tblWidgetPosition values(8,2.03,@LoginID)
 Insert tblWidgetPosition values(6,3.0,@LoginID)
 Insert tblWidgetPosition values(2,3.01,@LoginID)
 Insert tblWidgetPosition values(10,3.02,@LoginID)
 end

 --code ends

 FETCH NEXT FROM curLogin INTO @LoginID

 END

 CLOSE curLogin 

 DEALLOCATE curLogin 

 --cur2 ends

 FETCH NEXT FROM curDomain INTO @DomainID

 END

逐行插入可能会很慢。将数据准备到 CSV 文件中,然后使用 BULK INSERT 来完成这项工作。请注意数据中可能破坏插入内容的特殊字符。

BULK INSERT tblWidgetProperties
FROM 'c:\temp\WidgetProperties.tbl'  
WITH  
  (  
     FIELDTERMINATOR =',',  
     ROWTERMINATOR = '\n'
   );  

如果 BULK INSERT 不是一个选项,那么您应该监控是什么减慢了您的插入速度。 Disabling triggers 在您插入的表格上可能会有帮助。

您正在执行大量插入操作。 要降低插入量,请尝试创建两个临时表。每组刀片一个。然后你可以做类似的事情。

if not exists(select 1 from tblWidgetProperties where tblLogin_id = @LoginID)
 begin
    insert into tblWidgetProperties 
    Select [1],@LoginID,[2],[3]) from #tmpWidgetProperties
 end

if not exists(select 1 from tblWidgetPosition where tblLogins_id = @LoginID)
 begin
    Insert tblWidgetPosition 
    select [1], [2], @LoginID from #tmpWidgetPositions
 end

但在这样做之前,我会看一下 CTE 和 MERGE。

干杯马丁

你应该能够将它们写成两个插入,根本没有游标

类似于:

;WITH NewData AS (
   SELECT 1 as n UNION ALL
   SELECT 2 as n UNION ALL
   SELECT 3 as n UNION ALL
   SELECT 4 as n UNION ALL
   SELECT 5 as n UNION ALL
   SELECT 6 as n UNION ALL
   SELECT 7 as n UNION ALL
   SELECT 8 as n UNION ALL
   SELECT 9 as n UNION ALL
   SELECT 10 as n UNION ALL
   SELECT 12 as n
)
INSERT INTO tblWidgetProperties (/* Some column list, currently unknown */)
SELECT nd.n,tl.tbllogins_id,'isEnabled','true'
FROM
    NewData nd
       cross join
    tblLogins tl
WHERE
    tl.tbldomains_id in (select tbldomains_id from tblDomains) and
    tl.tbllogins_id not in (select tblLogin_id from tblWidgetProperties)

为 reader 留下的练习为另一个目标 table 执行基本相同的转换。如果数据每行不同,则在 NewData CTE 中添加更多列。如果所有行的数据都是固定的,请在 select 中保持内联值,如上所示。