SQL Server 2012中快速插入批量數據的示例及疑惑


SQL Server 2008中SQL應用系列--目錄索引

今天在做一個案例演示時,在SQL Server 2012中使用Insert語句插入1萬條數據,結果遇到了一個奇怪的現象,現將過程分享出來,以供有興趣的同學參考。


附:我的測試環境為:
SQL Server 2012,命名實例

Microsoft SQL Server 2012 - 11.0.2100.60 (Intel X86)
Feb 10 2012 19:13:17
Copyright (c) Microsoft Corporation
Enterprise Edition on Windows NT 6.0 <X86> (Build 6002: Service Pack 2)

 

創建示例數據庫

IF OBJECT_ID('DemoPager2012') IS NOT NULL
DROP DataBase DemoPager2012
GO

CREATE Database DemoPager2012
GO

USE DemoPager2012
GO

 

示例表,該表只有四個字段。

/*
Setup script to create the sample table and fill it with
sample data.
*/
IF OBJECT_ID('Customers','U') IS NOT NULL
DROP TABLE Customers

CREATE TABLE Customers ( CustomerID INT primary key identity(1,1),
CustomerNumber CHAR(4),
CustomerName VARCHAR(50),
CustomerCity VARCHAR(20) )
GO

 

現在展示批量插入10000條數據到該表中,語句如下:

TRUNCATE table Customers
GO

----清除干擾查詢
DBCC DROPCLEANBUFFERS
DBCC FREEPROCCACHE

SET STATISTICS IO ON;
SET STATISTICS TIME ON;
GO

DECLARE @d Datetime
SET @d=getdate();

declare @i int=1
while @i<=10000
begin
INSERT INTO Customers (CustomerNumber, CustomerName,
CustomerCity)
SELECT REPLACE(STR(@i, 4), ' ', '0'),'Customer ' + STR(@i,6),
CHAR(65 + (@i % 26)) + '-City'
set @i=@i+1
end

select [語句執行花費時間(毫秒)]=datediff(ms,@d,getdate())

SET STATISTICS IO OFF ;
SET STATISTICS TIME OFF;
GO

 

該插入語句在SQL Server 2008 r2版本和SQL Server 2012版本中,測試結果如下:

邀月工作室

邀月工作室

邀月工作室

 

 

令我驚訝的是,SQL Server 2012居然耗時達到5分多鍾,而SQL Server 2008R2版,只需要大約6秒鍾。更令人費解的是:查詢的I/O統計和elapsed time,在這兩個版本中幾乎一樣。對此異象,我只能理解為每次Insert時的毫秒級精度可能不足以度量該次操作帶來的細小差距,然而累積起來就非常可觀了。

解決方案一:使用 Set NoCount On,效果立竿見影

TRUNCATE table Customers
GO

----清除干擾查詢
DBCC DROPCLEANBUFFERS
DBCC FREEPROCCACHE

SET STATISTICS IO ON;
SET STATISTICS TIME ON;
GO

DECLARE @d Datetime
SET @d=getdate();
set nocount on
declare @i int=1
while @i<=10000
begin
INSERT INTO Customers (CustomerNumber, CustomerName,
CustomerCity)
SELECT REPLACE(STR(@i, 4), ' ', '0'),'Customer ' + STR(@i,6),
CHAR(65 + (@i % 26)) + '-City'
set @i=@i+1
end

select [語句執行花費時間(毫秒)]=datediff(ms,@d,getdate())

SET STATISTICS IO OFF ;
SET STATISTICS TIME OFF;
GO

 

邀月工作室

Set NoCount On(http://msdn.microsoft.com/zh-cn/library/ms189837.aspx)的作用:使返回的結果中不包含有關受 Transact-SQL 語句影響的行數的信息。這在批量插入時將顯著提高性能。至於 本例中,為什么SQL Server 2008 R2版中卻不受該開關影響,希望知道的同學不吝賜教,非常感謝。

 

改進解決方案二:使用 Set NoCount On+Transaction

TRUNCATE table Customers
GO

----清除干擾查詢
DBCC DROPCLEANBUFFERS
DBCC FREEPROCCACHE

 

SET STATISTICS IO ON;
SET STATISTICS TIME ON;
GO

DECLARE @d Datetime
SET @d=getdate();
set nocount on
declare @i int=1
BEGIN TRANSACTION
while @i<=10000
begin
INSERT INTO Customers (CustomerNumber, CustomerName,
CustomerCity)
SELECT REPLACE(STR(@i, 4), ' ', '0'),'Customer ' + STR(@i,6),
CHAR(65 + (@i % 26)) + '-City'
set @i=@i+1
end
COMMIT
select [語句執行花費時間(毫秒)]=datediff(ms,@d,getdate())

SET STATISTICS IO OFF ;
SET STATISTICS TIME OFF;
GO

 

邀月工作室

解決方案三:使用遞歸CTE插入

TRUNCATE table Customers
GO

DBCC DROPCLEANBUFFERS
DBCC FREEPROCCACHE

SET STATISTICS IO ON;
SET STATISTICS TIME ON;
GO

DECLARE @d Datetime
SET @d=getdate();

/*****運用CTE遞歸插入,速度較快,邀月注***********************/
WITH Seq (num,CustomerNumber, CustomerName, CustomerCity) AS
(SELECT 1,cast('0000'as CHAR(4)),cast('Customer 0' AS NVARCHAR(50)),cast('X-City' as NVARCHAR(20))
UNION ALL
SELECT num + 1,Cast(REPLACE(STR(num, 4), ' ', '0') AS CHAR(4)),
cast('Customer ' + STR(num,6) AS NVARCHAR(50)),
cast(CHAR(65 + (num % 26)) + '-City' AS NVARCHAR(20))
FROM Seq
WHERE num <= 10000
)
INSERT INTO Customers (CustomerNumber, CustomerName, CustomerCity)
SELECT CustomerNumber, CustomerName, CustomerCity
FROM Seq
OPTION (MAXRECURSION 0)

select [語句執行花費時間(毫秒)]=datediff(ms,@d,getdate())

SET STATISTICS IO OFF ;
SET STATISTICS TIME OFF;
GO

 

邀月工作室

邀月工作室

 

小結:SQL Server 2012中批量插入數據時,請記得Set NoCount ON,並盡可能加上Transaction,當然,推薦使用CTE,這可能會帶來性能上的巨大提升。

 邀月補充:

后來與微軟亞太工程師多次溝通,得出初步結論:

在不打開“set nocount on”時,SSMS 2012與SSMS 2008r2版本的UI在執行效率上可能有極大差異,而與SQL Server引擎沒有明顯相關。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM