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

来源:互联网 发布:网络贷款需要什么条件 编辑:程序博客网 时间:2024/06/03 14:52

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)


创建示例数据库

[sql] view plaincopyprint?
  1. IF OBJECT_ID('DemoPager2012')IS NOTNULL
  2. DROP DataBase DemoPager2012
  3. GO

  4. CREATE Database DemoPager2012
  5. GO

  6. USE DemoPager2012
  7. GO
示例表,该表只有四个字段。
[sql] view plaincopyprint?
  1. /*
  2. Setup script to create the sampletable and fill itwith
  3. sample data.
  4. */
  5. IF OBJECT_ID('Customers','U')IS NOTNULL
  6. DROP TABLE Customers

  7. CREATE TABLE Customers ( CustomerIDINT primarykey identity(1,1),
  8. CustomerNumber CHAR(4),
  9. CustomerName VARCHAR(50),
  10. CustomerCity VARCHAR(20) )
  11. GO
现在展示批量插入10000条数据到该表中,语句如下:
[sql] view plaincopyprint?
  1. TRUNCATE table Customers
  2. GO

  3. ----清除干扰查询
  4. DBCC DROPCLEANBUFFERS
  5. DBCC FREEPROCCACHE

  6. SET STATISTICS IOON;
  7. SET STATISTICSTIME ON;
  8. GO

  9. DECLARE @d Datetime
  10. SET @d=getdate();

  11. declare @i int=1
  12. while @i<=10000
  13. begin
  14. INSERT INTO Customers (CustomerNumber, CustomerName,
  15. CustomerCity)
  16. SELECT REPLACE(STR(@i, 4),' ', '0'),'Customer ' + STR(@i,6),
  17. CHAR(65 + (@i % 26)) + '-City'
  18. set @i=@i+1
  19. end

  20. select [语句执行花费时间(毫秒)]=datediff(ms,@d,getdate())

  21. SET STATISTICS IOOFF ;
  22. SET STATISTICSTIME OFF;
  23. GO

该插入语句在SQL Server 2008 r2版本和SQL Server 2012版本中,测试结果如下:

邀月工作室

令我惊讶的是,SQL Server 2012居然耗时达到5分多钟,而SQL Server 2008R2版,只需要大约6秒钟。更令人费解的是:查询的I/O统计和elapsed time,在这两个版本中几乎一样。对此异象,我只能理解为每次Insert时的毫秒级精度可能不足以度量该次操作带来的细小差距,然而累积起来就非常可观了。

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

[sql] view plaincopyprint?
  1. TRUNCATE table Customers
  2. GO

  3. ----清除干扰查询
  4. DBCC DROPCLEANBUFFERS
  5. DBCC FREEPROCCACHE

  6. SET STATISTICS IOON;
  7. SET STATISTICSTIME ON;
  8. GO

  9. DECLARE @d Datetime
  10. SET @d=getdate();
  11. set nocount on
  12. declare @i int=1
  13. while @i<=10000
  14. begin
  15. INSERT INTO Customers (CustomerNumber, CustomerName,
  16. CustomerCity)
  17. SELECT REPLACE(STR(@i, 4),' ', '0'),'Customer ' + STR(@i,6),
  18. CHAR(65 + (@i % 26)) + '-City'
  19. set @i=@i+1
  20. end

  21. select [语句执行花费时间(毫秒)]=datediff(ms,@d,getdate())

  22. SET STATISTICS IOOFF ;
  23. SET STATISTICSTIME OFF;
  24. GO

邀月工作室

Set NoCount On(http://msdn.microsoft.com/zh-cn/library/ms189837.aspx)的作用:使返回的结果中不包含有关受 Transact-SQL 语句影响的行数的信息。这在批量插入时将显著提高性能。至于 本例中,为什么SQL Server 2008 R2版中却不受该开关影响,希望知道的同学不吝赐教,非常感谢。

改进解决方案二:使用 Set NoCount On+Transaction

[sql] view plaincopyprint?
  1. TRUNCATE table Customers
  2. GO

  3. ----清除干扰查询
  4. DBCC DROPCLEANBUFFERS
  5. DBCC FREEPROCCACHE



  6. SET STATISTICS IOON;
  7. SET STATISTICSTIME ON;
  8. GO

  9. DECLARE @d Datetime
  10. SET @d=getdate();
  11. set nocount on
  12. declare @i int=1
  13. BEGIN TRANSACTION
  14. while @i<=10000
  15. begin
  16. INSERT INTO Customers (CustomerNumber, CustomerName,
  17. CustomerCity)
  18. SELECT REPLACE(STR(@i, 4),' ', '0'),'Customer ' + STR(@i,6),
  19. CHAR(65 + (@i % 26)) + '-City'
  20. set @i=@i+1
  21. end
  22. COMMIT
  23. select [语句执行花费时间(毫秒)]=datediff(ms,@d,getdate())

  24. SET STATISTICS IOOFF ;
  25. SET STATISTICSTIME OFF;
  26. GO

邀月工作室

解决方案三:使用递归CTE插入

[sql] view plaincopyprint?
  1. TRUNCATE table Customers
  2. GO

  3. DBCC DROPCLEANBUFFERS
  4. DBCC FREEPROCCACHE

  5. SET STATISTICS IOON;
  6. SET STATISTICSTIME ON;
  7. GO

  8. DECLARE @d Datetime
  9. SET @d=getdate();

  10. /*****运用CTE递归插入,速度较快,邀月注***********************/
  11. WITH Seq (num,CustomerNumber, CustomerName, CustomerCity)AS
  12. (SELECT 1,cast('0000'asCHAR(4)),cast('Customer 0'AS NVARCHAR(50)),cast('X-City'as NVARCHAR(20))
  13. UNION ALL
  14. SELECT num + 1,Cast(REPLACE(STR(num, 4),' ', '0')AS CHAR(4)),
  15. cast('Customer ' + STR(num,6)AS NVARCHAR(50)),
  16. cast(CHAR(65 + (num % 26)) +'-City' AS NVARCHAR(20))
  17. FROM Seq
  18. WHERE num <= 10000
  19. )
  20. INSERT INTO Customers (CustomerNumber, CustomerName, CustomerCity)
  21. SELECT CustomerNumber, CustomerName, CustomerCity
  22. FROM Seq
  23. OPTION (MAXRECURSION 0)

  24. select [语句执行花费时间(毫秒)]=datediff(ms,@d,getdate())

  25. SET STATISTICS IOOFF ;
  26. SET STATISTICSTIME OFF;
  27. GO

邀月工作室

小结:SQL Server 2012中批量插入数据时,请记得Set NoCount ON,并尽可能加上Transaction,当然,推荐使用CTE,这可能会带来性能上的巨大提升。

邀月补充:

后来与微软亚太工程师多次沟通,得出初步结论:

在不打开“set nocount on”时,SSMS 2012与SSMS 2008r2版本的UI在执行效率上可能有极大差异,而与SQL Server引擎没有明显相关。

邀月注:本文版权由邀月和CSDN共同所有,转载请注明出处。
助人等于自助! 3w@live.cn