如何提高 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 中保持内联值,如上所示。
我有一个 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 中保持内联值,如上所示。