您的位置:首页 > 其它

BCP导出导入大容量数据实践

2015-09-15 01:11 260 查看
前言

SQLSERVER提供多种不同的数据导出导入的工具,也可以编写SQL脚本,使用存储过程,生成所需的数据文件,甚至可以生成包含SQL语句和数据的脚本文件。各有优缺点,以适用不同的需求。下面介绍大容量数据导出导入的利器——BCP实用工具。同时在后面也介绍BULKINSERT导入大容量数据,以及BCP结合BULKINSERT做数据接口的实践(在SQL2008R2上实践)。

1.BCP的用法

BCP实用工具可以在MicrosoftSQLServer实例和用户指定格式的数据文件间大容量复制数据。使用BCP实用工具可以将大量新行导入SQLServer表,或将表数据导入数据文件。除非与queryout选项一起使用,否则使用该实用工具不需要了解Transact-SQL知识。BCP既可以在CMD提示符下运行,也可以在SSMS下执行。





figure-1

语法:

bcp{[[database_name.][schema].]{table_name|view_name}|"query"}
{in|out|queryout|format}data_file
[-mmax_errors][-fformat_file][-x][-eerr_file]
[-Ffirst_row][-Llast_row][-bbatch_size]
[-ddatabase_name][-n][-c][-N][-w][-V(70|80|90)]
[-q][-C{ACP|OEM|RAW|code_page}][-tfield_term]
[-rrow_term][-iinput_file][-ooutput_file][-apacket_size]
[-S[server_name[\instance_name]]][-Ulogin_id][-Ppassword]
[-T][-v][-R][-k][-E][-h"hint[,...n]"]




简单的导出例子1:





figure-2

简单的导出例子2:





figure-3

在SSMS上同时也可以执行:

EXEC[master]..xp_cmdshell
'BCPTestDB_2005.dbo.T1outE:\T1_02.txt-c-T'
GO


code-1





figure-4

EXEC[master]..xp_cmdshell
'BCP"SELECT*FROMTestDB_2005.dbo.T1"queryoutE:\T1_03.txt-c-T'
GO


code-2







figure-5

从个人来讲,我更喜欢使用第二种跟queryout选项一起使用的写法,因为这样可以更加灵活控制要导出的数据。如果执行BCP命令遇到这样的错误提示:

Msg15281,Level16,State1,Procedurexp_cmdshell,Line1
SQLServerblockedaccesstoprocedure'sys.xp_cmdshell'ofcomponent'xp_cmdshell'becausethiscomponentisturnedoffaspartofthesecurityconfigurationforthisserver.Asystemadministratorcanenabletheuseof'xp_cmdshell'byusingsp_configure.Formoreinformationaboutenabling'xp_cmdshell',see"SurfaceAreaConfiguration"inSQLServerBooksOnline.
基于安全的考虑,系统默认没有开启xp_cmdshell选项。使用下面语句开启此选项。

EXECsp_configure'showadvancedoptions',1
RECONFIGURE
GO

EXECsp_configure'xp_cmdshell',1
RECONFIGURE
GO


code-3

使用完之后,可以把sp_cmdshell关闭。

EXECsp_configure'showadvancedoptions',1
RECONFIGURE
GO

EXECsp_configure'xp_cmdshell',0
RECONFIGURE
GO


code-4

BCP导入数据

修改figure-2中的out为in即可,把数据导入。





figure-6





figure-7

使用BULKINSERT导入数据

BULKINSERTdbo.T1FROM'E:\T1.txt'
WITH(
FIELDTERMINATOR='\t',
ROWTERMINATOR='\n'
)


code-5





figure-8

关于BULKINSERT更详细的说明,参考:https://msdn.microsoft.com/zh-cn/library/ms188365%28v=sql.105%29.aspx

相比BCP的导入,BULKINSERT提供更灵活的选择。

BCP几个常用的参数说明:

database_name指定的表或视图所在数据库的名称。如果未指定,则使用用户的默认数据库。
in|out|queryout|formatin从文件复制到数据库表或视图。

out从数据库表或视图复制到文件。如果指定了现有文件,则该文件将被覆盖。提取数据时,请注意bcp实用工具将空字符串表示为null,而将null字符串表示为空字符串。

queryout从查询中复制,仅当从查询大容量复制数据时才必须指定此选项。

format根据指定的选项(-n-c-w-N)以及表或视图的分隔符创建格式化文件。大容量复制数据时,bcp命令可以引用一个格式化文件,从而避免以交互方式重复输入格式信息。format选项要求指定-f选项;创建XML格式化文件时还需要指定-x选项。

in从文件复制到数据库表或视图。
out从数据库表或视图复制到文件。如果指定了现有文件,则该文件将被覆盖。提取数据时,请注意bcp实用工具将空字符串表示为null,而将null字符串表示为空字符串。
queryout从查询中复制,仅当从查询大容量复制数据时才必须指定此选项。

-c使用字符数据类型执行该操作。此选项不提示输入每个字段;它使用char作为存储类型,不带前缀;使用\t(制表符)作为字段分隔符,使用\r\n(换行符)作为行终止符。
-w使用Unicode字符执行大容量复制操作。此选项不提示输入每个字段;它使用nchar作为存储类型,不带前缀;使用\t(制表符)作为字段分隔符,使用\n(换行符)作为行终止符。
-tfield_term指定字段终止符。默认值为\t(制表符)。使用此参数可以替代默认字段终止符。
-rrow_term指定行终止符。默认值为\n(换行符)。使用此参数可替代默认行终止符。
-Sserver_name[\instance_name]指定要连接的SQLServer实例。如果未指定服务器,则bcp实用工具将连接到本地计算机上的默认SQLServer实例。如果从网络或本地命名实例上的远程计算机中运行bcp命令,则必须使用此选项。若要连接到服务器上的SQLServer默认实例,请仅指定server_name。若要连接到SQLServer的命名实例,请指定server_name\instance_name。
-Ulogin_id指定用于连接到SQLServer的登录ID。
-Ppassword指定登录ID的密码。如果未使用此选项,bcp命令将提示输入密码。如果在命令提示符的末尾使用此选项,但不提供密码,则bcp将使用默认密码(NULL)。
-T指定bcp实用工具通过使用集成安全性的可信连接连接到SQLServer。不需要网络用户的安全凭据、login_id和password。如果未指定–T,则需要指定–U–P才能成功登录。
更详细的参数,请参考:https://msdn.microsoft.com/zh-cn/library/ms162802%28v=sql.105%29.aspx

2.实践

2.1导出数据

介绍完BCP的导出导入,以及BULKINSERT的导入,下面进行一些实际的操作。为了接近实际环境,创建一张10个字段的表,包含有几种常用的数据类型,构造2000万的数据,包含中文和英文。为了更快插入测试数据,先不创建索引。在执行下面代码之前,请留意下数据库的日志恢复模式是否设置为大容量模式或简单模式,以及磁盘空间是否足够(我的实践中,数据生成后数据文件和日志文件大概需要40G的空间)。

USEAdventureWorks2008R2
GO

IFOBJECT_ID(N'T1')ISNOTNULL
BEGIN
DROPTABLET1
END
GO

CREATETABLET1(
id_INT,
col_1NVARCHAR(50),
col_2NVARCHAR(40),
col_3NVARCHAR(40),
col_4NVARCHAR(40),
col_5INT,
col_6FLOAT,
col_7DECIMAL(18,8),
col_8BIT,
input_dateDATETIMEDEFAULT(GETDATE())
)
GO

WITHCTE1AS(
SELECTa.[object_id]FROMmaster.sys.all_objectsASa,master.sys.all_objectsASb,sys.databasesASc
WHEREc.database_id<=5
)

,CTE2AS(
SELECTROW_NUMBER()OVER(ORDERBY[object_id])asrow_noFROMCTE1
)

INSERTINTOT1(id_,col_1,col_2,col_3,col_4,col_5,col_6,col_7,col_8)
SELECTrow_no,REPLICATE(N'博客园',10),NEWID(),NEWID(),NEWID(),CAST(row_no*RAND()*10ASINT),row_no*RAND(),row_no*RAND(),CAST(row_no*RAND()ASINT)%2
FROMCTE2WHERErow_no<=20000000
GO


code-6

过程要花上几分钟的时间才能完成,请耐心等待一下。关于数据的构造,可以参考我的另一篇博文:/article/5227922.html

使用上面介绍的用法导出数据:

EXEC[master]..xp_cmdshell
'BCPAdventureWorks2008R2.dbo.T1outE:\T1_04.txt-w-T-SKEN\SQLSERVER08R2'
GO


code-7

这里使用-w参数。BCP可以在CMD下导出数据,测试导出2000万条记录,我的笔记本使用了近8分钟左右的时间。BCP同时也可以在SSMS中执行,使用了6分多钟时间,比CMD下速度要快些,生成的文件大小一致,每个文件近5GB。





figure-9





figure-10

而对于复杂的大容量导入情况,通常都会需要格式化文件。在以下情况下,必须使用格式化文件:

具有不同架构的多个表使用同一数据文件作为数据源。

数据文件中的字段数不同于目标表中的列数;例如:

目标表中至少包含一个定义了默认值或允许为NULL的列。

用户不具有对目标表的一个或多个列的SELECT/INSERT权限。

具有不同架构的两个或多个表使用同一个数据文件。

数据文件和表的列顺序不同。

数据文件列的终止字符或前缀长度不同。

这里不使用格式化文件进行导出导入的演示了。详细介绍与使用,请参考联机丛书。

2.2导入数据

使用BULKINSERT把数据导入到目标表数据。为提高性能,可临时删除索引,导完之后再重建索引等。请注意要预留足够的磁盘空间。这里大概花了15分钟导完。





figure-11

3.扩展

3.1数据导出导入自动化与数据接口

由于工作关系,有时要开发一些客户的数据接口,每天自动导入比较大量的数据。限制于应用程序等因素影响,所以考虑直接使用SQLSERVER的BULKINSERT每天自动去读取相关目录的中间文件。尽管目录是动态的,但由于中间文件是固定格式的,通过编写动态SQL,最后封装成存储过程,放到JOB中,配置运行的计划,即可完成自动化的工作。下面简单演示下过程:

3.1.1编写导入脚本

CREATEPROCEDUREsp_import_data
AS
BEGIN
DECLARE@pathNVARCHAR(500)
DECLARE@sqlNVARCHAR(MAX)
/*S_PARAMETERS表是可以在应用程序上配置路径的*/
SELECT@path=value_+CONVERT(NVARCHAR,getdate(),23)+'.txt'FROMS_PARAMETERSWHERE[type]='Import'
/*T4是一张临时的中间表。先把数据从文件中读入到中间表,最后通过脚本把T4中间表的数据插入到实际的业务表中*/
SET@sql=N'BULKINSERTT4FROM'''+@path+'''
WITH(
FIELDTERMINATOR=''*'',
ROWTERMINATOR=''\n''

)'
EXEC(@sql)
END
GO


code-8

3.1.2配置JOB

首先要配置好的是SQLSERVER有权限读取相关目录和文件的权限。在SqlServerConfigurationManager-->SQLServerServices选择相应的实例,右键选择属性,在LogOn页签,使用有足够权限启动SQLSERVER和有权限读取相关目录的用户,比如读取网络盘。



figure-12

在SQLServerAgent新建一个作业





figure-13

在General页,选择Owner,这里选择sa。





figure-14

在Steps页,在Command里执行写好的存储过程。





figure-15

在Schedules页,配置执行的时间和频率等。完成。





figure-16

3.2高版本数据库降级到低版本

一般来说,从低版本备份的数据库可以直接在高版本的数据库中恢复的,比如SQL2000的备份可以在SQL2005或SQL2008中恢复,除非是跨度太大的之外。比如SQL2000的备份就不能直接在SQL2012中恢复,只能恢复到SQL2008,再从SQL2008备份出来,最后到SQL2012上恢复。

而高版本的备份一般不能在低版本中恢复,如SQL2008的备份不能在SQL2005或SQL2000中恢复。而实际中,却又会遇到这种需求。最好是通过高版本SSMS直接连接两个不同版本的数据库,通过数据库间的数据导出导入或写脚本,把高版本的数据导到低版本的数据库中。这是比较快速安全的方法。但是如果两个版本的数据库不能相连,只能是把数据导出来,再导入。对于数据量不大来说,使用SSMS的导出导入功能,或是生成包含数据的脚本即可(下图)。对于大数据来说,却是一个灾难,如前面有2000万数据的大表,生成数据的脚本也有几个G大,直接使用SSMS执行是不可能的了。只能是使用SQLCMD实用工具,在后台执行SQL脚本,或者借助BCP、BULKINSERT等这种大容量数据导出导入的工具。





figure-17

4.总结

使用BCP并结合BULKINSERT可实现大容量数据的快速导出导入,并可以实现其自动化工作。对于少量数据来说,操作也不算很复杂。这是除了SSMS上的图形化工具之外,又一个非常实用的工具。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: