循环向SQL数据库插入上万条记录,探讨其效率问题

时间:2022-07-25 23:31:34
   本人是一个新手,本次和老师一起做一个AutoCAD的二次开发,设计到将DWG格式图片的所有实体信息录入的问题,我首先建立了一个专门用来连接数据库,对数据库进行相应的插入,编辑,删除等的类,读取到图片中的信息以后就通过该类来讲DWG图片中的图元信息插入数据库。 
    我使用的是SQL Server 2000,其中以上描述中主要涉及到的表有三个: 
第一个:保存图元的基本信息如下: 
GE_ID  int    自增(在两外两个表中要通过这个ID来关联) 
GE_Layer        后面两个是图元的信息 
GE_Handle 
第二个:记录对应图元的扩展信息 
GEE_Num  int  对应上述的GE_ID,表示对应的扩展信息归属于某一图元 
GEE_Extend      扩展信息 
第三个:记录图元坐标信息 
GEE_Num    int  对应上述的GE_ID 
GEE_X 
GEE_Y 
GEE_Z    坐标信息 

  通过这三个表保存图元的信息,采用的策略是先将基本的图元数据插入第一个表中,获取其自增的ID,然后将其他数据插入对应的两个表中。 
    首先说明下,我们一般设计到的DWG图片可能包含的图元至少有上万个,读取插入的数据量比较大,我采用的方法主要是读取一个实体插入一次(其中涉及到三个表的插入),程序还是一波三折的写完了,最后测试也通过了,数据可以很准确的保存到数据库中了,但是却有一个严重的问题,读取保存的速率慢的有点难以接受,开始还没有意识到问题出在数据保存到数据库的过程中,后来把有关向数据库保存数据的方法全部注释了才发现不用保存读取数据原来这么快。我想从这应该可以确定是数据保存上的效率很低了。之后我就开始琢磨如何提高大量插入的效率,查了很多资料,也看了很多论坛,看到了很多方法。 
    看了那么多后来想可能是我插入的太频繁了,所以我想把我读取到的同类信息串起来,作为一个字符串传到数据库存储过程中然后分开在保存(这也是网上看到的一种方法),我想大家看到这应该也知道我这的数据量应该是比较大的。串起来的字符串的长度应该也比较长(至少来说很容易突破8000),所以我选择了用一个text类型的存储过程参数来接受从程序中传来的字符串,但是又遇到一个比较严重的问题了,Sql 2000存储过程中text类型不支持字符串处理,最长的也只能用nvarchar(8000)了,更严重的是存储过程中也不能使用text类型的变量,这样我就无法把一个长度大于8000的text分成多个nvarchar(8000)了,整了许久没有结果只能来求救了。 
    网上也看到了用临时表来提高插入效率的,但是我不是很明白,如果有高手同意用这种方法的麻烦说清楚点。还有说法就是用多线程,这个我不是很会,如果可以的话也麻烦说清楚点。希望大家帮下忙,方法资料都可以,谢谢了。 
    

16 个解决方案

#1


这种情况,为什么不把数据放在内存,平时你的数据都在内存中处理,只有达到一定时间或一定数量以后才把数据从内存写入数据库等,不要一条条的写入

#2


定义一个数据结构,在使用一个链表或数组来保存要存储的信息。
当数量达到一定得时候,按1楼的办法,写入数据库中。

#3


相信从我的问题的描述中已经表明了我的解决思路和着类似,就是将需要插入数据库的数据转化成字符,串成字符串,然后一次性传入数据库,在数据库中再分解,相应的保存,但是遇到的问题是我要插入的数据非常多,达到上万条,如果串成字符串势必会很长,而存储过程中不能使用text,这样在存储过程中我就没有一个中间变量来分解传入的参数(sql server 200中),而只能用nvarchar(8000),最长只能用8000,这样就要想办法在程序里把要传入存储过程的字符串分成若干段分别传入了。这样也会影响到效率。
    所以我想寻求一种在程序中可以多次插入,到最后才一次性的提交给数据库,比如说用Recordset对象的updatebatch来实现批量插入(这我还在学习中,不知道行不行),希望大家说详细点,谢谢了。

#4


'需要插入数据库的数据转化成字符,串成字符串,然后一次性传入数据库,在数据库中再分解,相应的保存'
----------------------------------
为啥一定要用nvarchar呢,可以考虑用ntext类型来存储,就可以不必再分解了,关于vc读写ntext等大数据量字段的内容,网上有很多例子,你可以参考。

#5


此外,你说“存储过程中不能使用text”?
请参考
http://topic.csdn.net/t/20030830/21/2206539.html

#6


不是,我的意思是说在存储过程中不能申请text,ntext类型变量,例如:
Declare @Test text
就会出现如下的错误:
对于局部变量,text、ntext 和 image 数据类型无效。

我就是希望把读取一个SWG文件所要进行的上万次操作集合起来只进行一次操作,基本上是和数据库只进行一次交互,以此来调高数据保存的效率

#7


一个方案,如果你的待导入数据不很规范,就用VC转换输出成一个格式规范的文件,这个文件的格式要求可以参看数据库导入命令bcp支持的格式。然后调用bcp一次性导入这个文件。这样的性能和稳定性是很高的。

VC操作文件,bcp负责导入

#8


将你的字符串写成XML,
然后传给存储过程,存储过程是可以处理XML的。
我的SQL2005,所以没有字符长度最大为8000的限制

#9


引用 6 楼 hekejun19861107 的回复:
不是,我的意思是说在存储过程中不能申请text,ntext类型变量,例如:
Declare @Test text
就会出现如下的错误:
对于局部变量,text、ntext 和 image 数据类型无效。

我就是希望把读取一个SWG文件所要进行的上万次操作集合起来只进行一次操作,基本上是和数据库只进行一次交互,以此来调高数据保存的效率


没接触过BCP,不知可否说清楚点,给个例程之类的。而且我有一个疑问,我将读取到的数据一条一条的写到文件里,然后一次性导入,这之间和数据库交互的时间是少了,那写文件的时间会不会很长呢?两者加起来的时间会不会比原始方法更长呢?

#10


引用 6 楼 hekejun19861107 的回复:
不是,我的意思是说在存储过程中不能申请text,ntext类型变量,例如:
Declare @Test text
就会出现如下的错误:
对于局部变量,text、ntext 和 image 数据类型无效。

我就是希望把读取一个SWG文件所要进行的上万次操作集合起来只进行一次操作,基本上是和数据库只进行一次交互,以此来调高数据保存的效率


不好意思,引用错了。

没接触过BCP,不知可否说清楚点,给个例程之类的。而且我有一个疑问,我将读取到的数据一条一条的写到文件里,然后一次性导入,这之间和数据库交互的时间是少了,那写文件的时间会不会很长呢?两者加起来的时间会不会比原始方法更长呢?

#11


引用 7 楼 vieri_ch 的回复:
一个方案,如果你的待导入数据不很规范,就用VC转换输出成一个格式规范的文件,这个文件的格式要求可以参看数据库导入命令bcp支持的格式。然后调用bcp一次性导入这个文件。这样的性能和稳定性是很高的。

VC操作文件,bcp负责导入


没接触过BCP,不知可否说清楚点,给个例程之类的。而且我有一个疑问,我将读取到的数据一条一条的写到文件里,然后一次性导入,这之间和数据库交互的时间是少了,那写文件的时间会不会很长呢?两者加起来的时间会不会比原始方法更长呢?

【哎,真是献丑了,竟然引用两次都点错……】

#12


引用 11 楼 hekejun19861107 的回复:
引用 7 楼 vieri_ch 的回复:
一个方案,如果你的待导入数据不很规范,就用VC转换输出成一个格式规范的文件,这个文件的格式要求可以参看数据库导入命令bcp支持的格式。然后调用bcp一次性导入这个文件。这样的性能和稳定性是很高的。

VC操作文件,bcp负责导入


没接触过BCP,不知可否说清楚点,给个例程之类的。而且我有一个疑问,我将读取到的数据一条一条的写到文件里,然后一次性导入,这之间和数据库交互的时间是少了,那写文件的时间会不会很长呢?两者加起来的时间会不会比原始方法更长呢?

【哎,真是献丑了,竟然引用两次都点错……】

写文件的时间不会长的,除非文件很大,你可以测试一下写个100M的文件多长时间
写数据库不只单纯的写文件,还包括网络传输

另外跟你连接数据库的方式也有关系,ODBC就慢,ADO就快多了,最快应该就是用DBLibrary API直接操作数据库了,另外你插入数据的操作是什么样的,我见过的有个程序是每次插入要执行连接数据库,插入,关闭数据库连接,那样肯定奇慢无比,时间都浪费在网络上了,希望lz不是这种方式

#13


可以考虑保存图片的内存映像 (image),如果数据量太大,可以在保之前用zlib压缩一下
这样,一张图片,就是一条记录,从数据库读取的内存映像,如果涉及到指针,需要使用偏移值保存,以便快速读取再建内存映像

不管是什么样的数据库,读写上万条的数据,都是需要花费一些时间的,办法不会太多:)

不能在存储过程中声明局部的text变量,但是可以向存储过程传送text,text是一个巨大的指针,部分字符串操作函数,写入数据库需要通过指针(大于8000)

多条记录insert 可以考虑使用union

#14


在我公司使用的一套大型系统,公司不是IT类的,这个系统是异构混合的商用系统,数据量很大,处理的记录也达到百万级/每个月。具体代码就太复杂了。我先讲清楚解决方案。

文件读写IO对于VC来说是强项。数据库的读写,查询,则是VC程序中很容易成为瓶颈的地方。

这个解决方案的时间开销,是文件IO+BCP导入。BCP导入是微软的数据库产品提供的功能,稳定性不必说,从效率来说,他之所以快是因为成块读写,减少了循环的次数。

好比格式规范的待导入文件,是一行一行的记录,但是对于BCP的内部,对于规范数据是成块读入,并写入数据库。甚至绕过了insert这样的语句。不是简单的insert的再次封装。

当然我们如果自己可以实现BCP的功能,就不必借文件做中转。但是很显然,微软提供的BCP应该已经做到可能最高效率了。我们不用做多余的工作。

bcp比程序自己循环insert快。这是已经有过的经典讨论的结果。

接下来,我们讨论文件IO的问题。事实上,我们可参照块读写。对于插入到文件,不要一条条的来,而是在内存中缓存记录,1000条或者多少条,以50M为单位块写入文件,1GM的数据(超过100万条记录)我们仅仅循环20000次,这是单线程的情况。这个代码级的开销已经提高很多了。

成批成块读写到数据库则交给BCP,我们不必再次实现,因为数据库的最终写入的优化取决于微软的SQL server

你可以考虑下我的分析,确定这个方案是否有效。

如果想测试,不用VC,用个VBS使用ADO,insert,10000行记录到数据库,然后用BCP导入10000行记录到数据库,你可以比较一下,当然测试IO,你可以简单的用VBS来测试生成一个10000行记录的文件。

时间上做个比较就出来了。这和VC已经关系不大,而是解决方案的问题了,很多商用系统都是这么做的。

#15


http://topic.csdn.net/u/20090203/14/71837963-1af6-4285-ae3f-8b816f264ca6.html
这是BCP导入二进制文件的一个参考,我曾经参考过,你看一下。
你如果涉及图元数据,存在二进制数据,最好还是这样比较好

#16


http://topic.csdn.net/u/20090203/14/71837963-1af6-4285-ae3f-8b816f264ca6.html

#1


这种情况,为什么不把数据放在内存,平时你的数据都在内存中处理,只有达到一定时间或一定数量以后才把数据从内存写入数据库等,不要一条条的写入

#2


定义一个数据结构,在使用一个链表或数组来保存要存储的信息。
当数量达到一定得时候,按1楼的办法,写入数据库中。

#3


相信从我的问题的描述中已经表明了我的解决思路和着类似,就是将需要插入数据库的数据转化成字符,串成字符串,然后一次性传入数据库,在数据库中再分解,相应的保存,但是遇到的问题是我要插入的数据非常多,达到上万条,如果串成字符串势必会很长,而存储过程中不能使用text,这样在存储过程中我就没有一个中间变量来分解传入的参数(sql server 200中),而只能用nvarchar(8000),最长只能用8000,这样就要想办法在程序里把要传入存储过程的字符串分成若干段分别传入了。这样也会影响到效率。
    所以我想寻求一种在程序中可以多次插入,到最后才一次性的提交给数据库,比如说用Recordset对象的updatebatch来实现批量插入(这我还在学习中,不知道行不行),希望大家说详细点,谢谢了。

#4


'需要插入数据库的数据转化成字符,串成字符串,然后一次性传入数据库,在数据库中再分解,相应的保存'
----------------------------------
为啥一定要用nvarchar呢,可以考虑用ntext类型来存储,就可以不必再分解了,关于vc读写ntext等大数据量字段的内容,网上有很多例子,你可以参考。

#5


此外,你说“存储过程中不能使用text”?
请参考
http://topic.csdn.net/t/20030830/21/2206539.html

#6


不是,我的意思是说在存储过程中不能申请text,ntext类型变量,例如:
Declare @Test text
就会出现如下的错误:
对于局部变量,text、ntext 和 image 数据类型无效。

我就是希望把读取一个SWG文件所要进行的上万次操作集合起来只进行一次操作,基本上是和数据库只进行一次交互,以此来调高数据保存的效率

#7


一个方案,如果你的待导入数据不很规范,就用VC转换输出成一个格式规范的文件,这个文件的格式要求可以参看数据库导入命令bcp支持的格式。然后调用bcp一次性导入这个文件。这样的性能和稳定性是很高的。

VC操作文件,bcp负责导入

#8


将你的字符串写成XML,
然后传给存储过程,存储过程是可以处理XML的。
我的SQL2005,所以没有字符长度最大为8000的限制

#9


引用 6 楼 hekejun19861107 的回复:
不是,我的意思是说在存储过程中不能申请text,ntext类型变量,例如:
Declare @Test text
就会出现如下的错误:
对于局部变量,text、ntext 和 image 数据类型无效。

我就是希望把读取一个SWG文件所要进行的上万次操作集合起来只进行一次操作,基本上是和数据库只进行一次交互,以此来调高数据保存的效率


没接触过BCP,不知可否说清楚点,给个例程之类的。而且我有一个疑问,我将读取到的数据一条一条的写到文件里,然后一次性导入,这之间和数据库交互的时间是少了,那写文件的时间会不会很长呢?两者加起来的时间会不会比原始方法更长呢?

#10


引用 6 楼 hekejun19861107 的回复:
不是,我的意思是说在存储过程中不能申请text,ntext类型变量,例如:
Declare @Test text
就会出现如下的错误:
对于局部变量,text、ntext 和 image 数据类型无效。

我就是希望把读取一个SWG文件所要进行的上万次操作集合起来只进行一次操作,基本上是和数据库只进行一次交互,以此来调高数据保存的效率


不好意思,引用错了。

没接触过BCP,不知可否说清楚点,给个例程之类的。而且我有一个疑问,我将读取到的数据一条一条的写到文件里,然后一次性导入,这之间和数据库交互的时间是少了,那写文件的时间会不会很长呢?两者加起来的时间会不会比原始方法更长呢?

#11


引用 7 楼 vieri_ch 的回复:
一个方案,如果你的待导入数据不很规范,就用VC转换输出成一个格式规范的文件,这个文件的格式要求可以参看数据库导入命令bcp支持的格式。然后调用bcp一次性导入这个文件。这样的性能和稳定性是很高的。

VC操作文件,bcp负责导入


没接触过BCP,不知可否说清楚点,给个例程之类的。而且我有一个疑问,我将读取到的数据一条一条的写到文件里,然后一次性导入,这之间和数据库交互的时间是少了,那写文件的时间会不会很长呢?两者加起来的时间会不会比原始方法更长呢?

【哎,真是献丑了,竟然引用两次都点错……】

#12


引用 11 楼 hekejun19861107 的回复:
引用 7 楼 vieri_ch 的回复:
一个方案,如果你的待导入数据不很规范,就用VC转换输出成一个格式规范的文件,这个文件的格式要求可以参看数据库导入命令bcp支持的格式。然后调用bcp一次性导入这个文件。这样的性能和稳定性是很高的。

VC操作文件,bcp负责导入


没接触过BCP,不知可否说清楚点,给个例程之类的。而且我有一个疑问,我将读取到的数据一条一条的写到文件里,然后一次性导入,这之间和数据库交互的时间是少了,那写文件的时间会不会很长呢?两者加起来的时间会不会比原始方法更长呢?

【哎,真是献丑了,竟然引用两次都点错……】

写文件的时间不会长的,除非文件很大,你可以测试一下写个100M的文件多长时间
写数据库不只单纯的写文件,还包括网络传输

另外跟你连接数据库的方式也有关系,ODBC就慢,ADO就快多了,最快应该就是用DBLibrary API直接操作数据库了,另外你插入数据的操作是什么样的,我见过的有个程序是每次插入要执行连接数据库,插入,关闭数据库连接,那样肯定奇慢无比,时间都浪费在网络上了,希望lz不是这种方式

#13


可以考虑保存图片的内存映像 (image),如果数据量太大,可以在保之前用zlib压缩一下
这样,一张图片,就是一条记录,从数据库读取的内存映像,如果涉及到指针,需要使用偏移值保存,以便快速读取再建内存映像

不管是什么样的数据库,读写上万条的数据,都是需要花费一些时间的,办法不会太多:)

不能在存储过程中声明局部的text变量,但是可以向存储过程传送text,text是一个巨大的指针,部分字符串操作函数,写入数据库需要通过指针(大于8000)

多条记录insert 可以考虑使用union

#14


在我公司使用的一套大型系统,公司不是IT类的,这个系统是异构混合的商用系统,数据量很大,处理的记录也达到百万级/每个月。具体代码就太复杂了。我先讲清楚解决方案。

文件读写IO对于VC来说是强项。数据库的读写,查询,则是VC程序中很容易成为瓶颈的地方。

这个解决方案的时间开销,是文件IO+BCP导入。BCP导入是微软的数据库产品提供的功能,稳定性不必说,从效率来说,他之所以快是因为成块读写,减少了循环的次数。

好比格式规范的待导入文件,是一行一行的记录,但是对于BCP的内部,对于规范数据是成块读入,并写入数据库。甚至绕过了insert这样的语句。不是简单的insert的再次封装。

当然我们如果自己可以实现BCP的功能,就不必借文件做中转。但是很显然,微软提供的BCP应该已经做到可能最高效率了。我们不用做多余的工作。

bcp比程序自己循环insert快。这是已经有过的经典讨论的结果。

接下来,我们讨论文件IO的问题。事实上,我们可参照块读写。对于插入到文件,不要一条条的来,而是在内存中缓存记录,1000条或者多少条,以50M为单位块写入文件,1GM的数据(超过100万条记录)我们仅仅循环20000次,这是单线程的情况。这个代码级的开销已经提高很多了。

成批成块读写到数据库则交给BCP,我们不必再次实现,因为数据库的最终写入的优化取决于微软的SQL server

你可以考虑下我的分析,确定这个方案是否有效。

如果想测试,不用VC,用个VBS使用ADO,insert,10000行记录到数据库,然后用BCP导入10000行记录到数据库,你可以比较一下,当然测试IO,你可以简单的用VBS来测试生成一个10000行记录的文件。

时间上做个比较就出来了。这和VC已经关系不大,而是解决方案的问题了,很多商用系统都是这么做的。

#15


http://topic.csdn.net/u/20090203/14/71837963-1af6-4285-ae3f-8b816f264ca6.html
这是BCP导入二进制文件的一个参考,我曾经参考过,你看一下。
你如果涉及图元数据,存在二进制数据,最好还是这样比较好

#16


http://topic.csdn.net/u/20090203/14/71837963-1af6-4285-ae3f-8b816f264ca6.html