一定数量的列后,SQL Server 中的 INSERT 性能下降

And*_*bka 4 performance sql-server insert sql-server-2014

我正在开发一个具有将表格数据导出到指定数据库表的功​​能的应用程序。该应用程序使用INSERT语句将其数据导出到目标数据库。

插入是通过一个批处理INSERT语句完成的,每个 SQL 语句有 100 行INSERT(现在我不能使用BULK INSERTbcp)。

我注意到,当源数据中的列数超过某个数字(该数字不是固定的,取决于值的大小、每个中的行数INSERT等)时,导出时间会不成比例地增加。

例如,导出 50 000 行(500 条INSERT语句,每条语句 100 行)的随机字符串,每个字符串有 100 个字符,每个INSERT需要100 行:

3 秒,5 列
6 秒,10 列
56 秒,15 列
77 秒,20 列

请注意 10 列和 15 列之间的导出时间差异。我原以为 15 列的导出时间为 9-10 秒,但实际上要长 5 倍。在测试其他数据集的导出时,我发现了类似的性能下降。

为了确保问题不在我这边,我INSERT通过sqlcmd.exe. 我得到了类似的结果。

问题:如何让 SQL Server 像处理小列一样快速处理大量列?或者至少将性能下降的点“移动”到更多的列?

额外细节:

  • INSERT 查询是在本地 SQL Server Express 2014(64 位)版本 12.0.5000.0 上执行的;
  • 数据库恢复模式设置为简单;
  • 所有INSERT语句都包含在一个事务中(我尝试COMMIT在 each 之后调用INSERT,但结果几乎相同);
  • 在每次测试之前创建目标表。它是一个简单的表,没有任何索引、外键、约束等;
  • 硬盘驱动器性能似乎不是问题的根源,因为在前两次测试(5 列和 10 列)sqlservr.exe期间,进程的磁盘写入速度是后两次情况下的 10 倍。

表是这样创建的:

CREATE TABLE [Test_Table]
(
    [Column 1] VARCHAR(255),
    [Column 2] VARCHAR(255),
    [Column 3] VARCHAR(255),
    [Column 4] VARCHAR(255),
    [Column 5] VARCHAR(255)
)
Run Code Online (Sandbox Code Playgroud)

数据看起来像这样(每个单元格实际上包含 100 个字符的长字符串,同一行中的所有字符串都相等):

+------------+------------+------------+------------ --+------------+
| [第1栏] | [第2栏] | [第3栏] | [第4栏] | [第5栏] |
+------------+------------+------------+--------- --+------------+
| R6YZ..uWaQ | R6YZ..uWaQ | R6YZ..uWaQ | R6YZ..uWaQ | R6YZ..uWaQ |
| DMNW..Kh0a | DMNW..Kh0a | DMNW..Kh0a | DMNW..Kh0a | DMNW..Kh0a |
| GKbg..yuap | GKbg..yuap | GKbg..yuap | GKbg..yuap | GKbg..yuap |
| pG+f..64bX | pG+f..64bX | pG+f..64bX | pG+f..64bX | pG+f..64bX |
| O2Q7..fTNF | O2Q7..fTNF | O2Q7..fTNF | O2Q7..fTNF | O2Q7..fTNF |

以下是重现该问题的两个示例:

http://rextester.com/OZI56670(10列,~0.09 秒)
http://rextester.com/HLAP4972(11列,~0.45 秒)

Mar*_*ith 5

您发布的 10 列 100 行和 11 列 100 行的再现之间的区别在于,第一个的执行计划使用Simple Parameterization

10 列的实际执行计划列出了从@1到 的参数@1000

11 * 1001100。但是一千似乎是自动参数化查询可以达到的最大参数数。

您正在为每个插入 10 次。在 10 列的情况下,计划可以编译一次并重新用于其他 9 个插入。在 11 列的情况下,每个插入语句都需要单独编译。

此外,当 SQL Server 需要查看文字值时,编译过程需要更长的时间,因为它花费时间计算组的属性(或者至少曾经是这种情况,我不确定这是否在最近的版本中发生了变化)。