如何运行带有许多插入的大型脚本而又不会耗尽内存?


28

题:

我有一个从select语句中插入约4.5万个脚本的脚本。当我尝试运行它时,收到一条错误消息,指出我的内存不足。如何使该脚本运行?

内容:

  1. 添加了一些新的数据字段,以使一个应用程序可以与客户端使用的另一个应用程序完美兼容。
  2. 从客户端获取了一个包含所有数据的电子表格,这些数据将当前数据项映射到这些新字段的值。
  3. 将电子表格转换为插入语句。
  4. 如果我仅运行某些语句,则它可以工作,但整个脚本却不能。
  5. 不,没有错别字。

如果有其他方法,我应该加载此数据,请随时批评我并告知我。


SO上的类似问题:(stackoverflow.com/questions/222442/…)不确定答案是否有帮助
jumpdart '04

Answers:


17

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

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

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


2
一个GO说法?好吧,我想您是否正在使用其他确定的脚本生成它们。否则,我每隔1000 INSERT秒就放一个。关于使事务原子化并最大程度地减少事务的大小,为什么不将所有行加载到临时表或表变量中,然后从那里一次加载到目标表?
Nick Chammas

1000和1一样好,但更难计算。老实说,他可能只在中间位置接近21,500条声明而放弃了一条GO声明。我喜欢GO修复程序,因为它不需要对当前脚本进行复杂的编辑,也不需要计算INSERT语句(它可能不会直接映射到行号)。
达林海峡2012年

2
当然,即使差一点地近似1000条语句也足够了。:)
Nick Chammas 2012年

1
添加GO是一个快速简便的修复程序。25mb脚本在不到9分钟的时间内运行就没有问题。希望将其作为脚本保存在我们的标准修补程序部署过程中,以确保它不出现问题。
spaghetticowboy 2012年

14

BULK INSERT还是bcp比45,000个插入语句更合适的选项。

如果您需要坚持使用insert语句,则可以考虑以下几种选择:

答:使用事务并在每条中包装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

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

为了简洁起见,我省略了错误处理,但要点是,我绝不会尝试将单个45,000条单独的语句发送到SQL Server。


1
不幸的是,OP无法使用表值构造函数(2008或更高版本的功能)。他仍然必须将插入片段分为1000行,这是您可以与TVC一起分组的最大值。
Nick Chammas 2012年

直到看到版本标签,这将是我的第一个建议。
亚伦·伯特兰

2
@NickChammas- 这些的性能随着值子句BTW的数量非线性地降低。我提交了一个连接项目,其副本是在2008年的2008开发实例上以1000 分钟VARCHAR(800)编译时间在2008年的dev实例上插入了1000行,其中包含10 列,因为它做了很多不必要的比较值的工作,而不是继续插入值(执行很多操作)参数化时更快,没有值可查看)。尽管在2012年有了很大的改进,但非线性模式仍然存在并且应在以后的版本中进行修复。
马丁·史密斯

9

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

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

SSMS导入数据任务。


多数民众赞成在帮助,但我没有访问客户端数据库。我必须准备脚本中的补丁程序和数据加载
spaghetticowboy


0

是的,我们可以做到这一点,我尝试使用BCP(大容量复制程序)方法来避免内存不足的问题。

注意:在SQL Server 2014上尝试过。

在BCP中,首先我们需要将源数据库数据导出到bcp文件(在本地目录文件夹中),然后需要将该bcp文件导入到目标数据库。

在此处输入图片说明

以下是步步高升的步骤:

注意:

a)确保目标数据库中存在空表

b)确保C盘中存在Temp文件夹

  1. 使用如下所示的命令创建一个名为Export_Data.bat的蝙蝠文件:

    bcp.exe [Source_DataBase_Name].[dbo].[TableName] OUT "C:\Temp\TableName.bcp" -S "Computer Name" -U "SQL Server UserName" -P "SQL Server Password" -n -q 

    暂停

  2. 运行该bat文件,结果将在Temp文件夹中生成一个bcp文件

  3. 然后使用以下命令创建另一个名为Import_Data.bat的蝙蝠文件:

    bcp.exe [Destination_DataBase_Name].[dbo].[TableName] IN "C:\Temp\TableName.bcp" -S "Computer Name" -U "SQL Server UserName" -P "SQL Server Password" -n -q 

    暂停

现在我们开始!


得到错误“输入,输出或格式选项需要有效的表名。” 尝试导出数据时。
参议员雅各布

1
能否将所有属性值都粘贴到您尝试过的命令中。请遵循以下示例:bcp.exe ExportDB.dbo.AddressCountry OUT“ C:\ Temp \ AddressCountry.bcp” -S“ IN-L20054” -U“ sa” -P“ sa” -n -q在那[ExportDB->源数据库,AddressCountry->源数据库中存在的表,IN-L20054->机器名,“ sa”是数据库的用户名/密码]
Kms

我现在没有。我最终在SSMS中使用了导入数据功能。然后使用MS OLE DB连接将目标DB(v14.0)连接到源DB(v.15.0),导入数百万行数据的速度非常快。谢谢!
森·雅各布
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.