如何在不耗尽内存的情况下运行包含许多插入内容的大型脚本?

spa*_*boy 29 sql-server-2005 sql-server etl

题:

我有一个脚本,其中包含来自 select 语句的大约 45,000 个插入。当我尝试运行它时,我收到一条错误消息,指出我的内存不足。我怎样才能运行这个脚本?

语境:

  1. 添加了一些新的数据字段,使应用程序与客户端使用的另一个应用程序配合得很好。
  2. 从客户端获得了一份数据电子表格,其中包含将当前数据项映射到这些新字段的值的数据。
  3. 将电子表格转换为插入语句。
  4. 如果我只运行一些语句,它会起作用,但整个脚本不会。
  5. 没有。没有错别字。

如果有不同的方式我应该加载这些数据,请随时批评我并让我知道。

Dar*_*ait 18

SQL Server 2005 的最大批处理大小为 65,536 * 网络数据包大小 (NPS),其中 NPS 通常为 4KB。结果是 256 MB。这意味着每个插入语句的平均大小为 5.8 KB。这似乎不对,但也许那里有多余的空间或不寻常的东西。

我的第一个建议是在每个 INSERT 语句之后放置一个“GO”语句。这会将您的单批 45,000 个 INSERT 语句分成 45,000 个单独的批次。这应该更容易消化。小心,如果其中一个插入失败,您可能很难找到罪魁祸首。您可能希望通过交易来保护自己。如果您的编辑器具有良好的搜索和替换功能(可以让您搜索和替换诸如 \r\n 之类的返回字符)或宏功能,则您可以快速添加这些语句。

第二个建议是使用向导直接从 Excel 导入数据。该向导会在幕后为您构建一个小的 SSIS 包,然后运行它。不会有这个问题。

  • *每个*语句之后的`GO`?好吧,我想如果您使用另一个脚本生成它们,那没问题。否则,我只会在每 1000 次`INSERT` 之后放一个。关于使事务原子化并最小化事务的大小,为什么不将所有行加载到临时表或表变量中,然后从那里一次性将它们加载到目标表? (2认同)
  • 当然,即使是 1000 条语句的糟糕近似值也足够了。:) (2认同)

Aar*_*and 14

BULK INSERT或者bcp看起来比 45,000 个插入语句更合适的选项。

如果您需要坚持使用插入语句,我会考虑几个选项:

A:使用事务,每条语句打包成100或500或1000条语句的批次,以尽量减少对日志和批次的影响。例如

BEGIN TRANSACTION;
INSERT dbo.table(a, ...) SELECT 1, ...
INSERT dbo.table(a, ...) SELECT 2, ...
...
INSERT dbo.table(a, ...) SELECT 500, ...
COMMIT TRANSACTION;
GO

BEGIN TRANSACTION;
INSERT dbo.table(a, ...) SELECT 1, ...
INSERT dbo.table(a, ...) SELECT 2, ...
...
INSERT dbo.table(a, ...) SELECT 500, ...
COMMIT TRANSACTION;
GO
Run Code Online (Sandbox Code Playgroud)

B: 一次使用UNION ALL100 或 500 条语句,而不是单独的插入语句,例如

INSERT dbo.table(a, ...)
SELECT 1, ...
UNION ALL SELECT 2, ...
...
UNION ALL SELECT 500, ...
GO

INSERT dbo.table(a, ...)
SELECT 501, ...
UNION ALL SELECT 502, ...
...
UNION ALL SELECT 1000, ...
GO
Run Code Online (Sandbox Code Playgroud)

为简洁起见,我省略了错误处理,但重点是我永远不会尝试向 SQL Server 发送一批 45,000 个单独的语句。

  • @NickChammas - [BTW 的性能随着值子句的数量呈非线性下降](http://stackoverflow.com/a/8640583)。我提交了 [一个连接项目](http://ow.ly/aquPm),并在 2008 年插入了 1000 行和 10 个 `VARCHAR(800)` 列,在 2008 年的**编译**时间为 12.5 分钟dev 实例,因为它做了很多不必要的比较值的工作,而不是仅仅继续插入它们(在参数化并且没有要查看的值时执行得更快)。尽管在 2012 年有了很大改进,但非线性模式仍然存在,应该在之后的版本中修复。 (2认同)

dat*_*god 9

我不确定为什么会出现内存不足错误,但有一种更简单的方法。

如果您可以将电子表格中的数据导出为分隔格式(例如 csv),您可以使用 SSMS 中的数据导入向导为您插入数据:

SSMS 导入数据任务。