带有标识(自动增量)列的批量插入


74

我正在尝试从CSV文件向数据库中添加批量数据。

Employee表的列ID(PK)自动增加。

CREATE TABLE [dbo].[Employee](
 [id] [int] IDENTITY(1,1) NOT NULL,
 [Name] [varchar](50) NULL,
 [Address] [varchar](50) NULL
) ON [PRIMARY]

我正在使用此查询:

BULK INSERT Employee  FROM 'path\tempFile.csv ' 
WITH (FIRSTROW = 2,KEEPIDENTITY,FIELDTERMINATOR = ',' , ROWTERMINATOR = '\n');

.CSV文件-

Name,Address
name1,addr test 1
name2,addr test 2

但它导致此错误消息:

第2行第1列(id)的批量加载数据转换错误(类型不匹配或指定代码页的字符无效)。


您能否将您的示例数据发布到csv文件中
praveen 2012年

1
我一直在寻找那个KEEPIDENTITY东西...谢谢!
nrod

1
实际上,有一个非常简单的解决方案。创建一个视图,仅省略ID列,然后将您的批量插入视图中。
Charles Okwuagwu

Answers:


49

不要直接将BULK INSERT插入真实表中。

我会一直

  1. 从CSV文件插入到临时dbo.Employee_Staging(无IDENTITY列)中
  2. 可能编辑/清理/处理您的导入数据
  3. 然后使用T-SQL语句将数据复制到真实表中:

    INSERT INTO dbo.Employee(Name, Address) 
       SELECT Name, Address
       FROM dbo.Employee_Staging
    

1
这个主意不错,但是为什么要使用登台表呢?
阿比(Abhi)2012年

7
@Abhi:我可以(1)删除引起悲伤的IDENTITY列,以及(2)在实际导入到实际表中之前,可以查看数据,可能删除某些行,更新某些行。
marc_s 2012年

11
@marc_s虽然这是一个很好的建议,但这并不能回答问题。jwerts应该真的是最好的答案。
莫本

好的一般建议。但是,即使有了临时表,如果要从输入文件中记录行号怎么办?
2015年

>不要将INSERT直接插入真实表中。-这个建议的有效期为MSSQL数据库只,也有例如Postgres里没有这样的限制
ARA1307

101

将id列添加到csv文件,并将其留空:

id,Name,Address
,name1,addr test 1
,name2,addr test 2

从查询中删除KEEPIDENTITY关键字:

BULK INSERT Employee  FROM 'path\tempFile.csv ' 
WITH (FIRSTROW = 2,FIELDTERMINATOR = ',' , ROWTERMINATOR = '\n');

ID身份字段将自动增加。

如果您将值分配给csv中的id字段,除非您使用KEEPIDENTITY关键字,否则它们将被忽略,然后将使用它们代替自动递增。


即使使用临时表,如果您想从输入文件中记录行号,也将需要这样。我喜欢!
2015年

嗨@Josh Werts。到目前为止,您的解决方案对我来说是一种祝福。它适用于我的本地数据库。现在,当我使用Microsoft SQL Server Management Studio连接到远程数据库并运行命令时,出现错误“由于无法打开文件“ D:\ data.csv”而无法批量加载。操作系统错误代码21(设备尚未准备好。)csv文件必须在具有DB的同一服务器上。csv文件在我的计算机上
Fokwa Best

1
@FokwaBest-我想远程服务器没有您的D:驱动器的概念。我认为您需要创建一个共享文件夹,远程服务器可以访问该共享文件夹,然后以这种方式引用它。...类似\\ myshare \ data.csv。我在这里不是专家,实际上在sql server中工作不多,所以也许其他人可以回答,如果这不起作用。
乔什·沃茨

解决方案很好,但是如果您无法更新csv文件,该怎么办?如果有条件您必须下载文件并将其上传到db,在这种情况下我们应该怎么办?
萨迪亚

32

我遇到了类似的问题,但是我需要确保ID的顺序与源文件中的顺序一致。我的解决方案是对大容量插入使用VIEW:

保持表格不变并创建此VIEW(选择“ ID”列以外的所有内容)

CREATE VIEW [dbo].[VW_Employee]
AS
SELECT [Name], [Address]
FROM [dbo].[Employee];

然后,您的BULK INSERT应该如下所示:

BULK INSERT [dbo].[VW_Employee] FROM 'path\tempFile.csv ' 
WITH (FIRSTROW = 2,FIELDTERMINATOR = ',' , ROWTERMINATOR = '\n');

2
到目前为止,这是对该问题的最佳解决方案
DhruvJoshi

我同意这是这样做的方法。
德里克·哈克特

2
将批量插入运行到与源文件不匹配的登台表中的最简单,最简单的方法。但是,我今天才发现,显然不能保证将按文件顺序插入数据。这是导入大型机头文件/详细信息类型文件的杀手
Nick.McDermaid

9

您必须使用格式文件进行批量插入:

   BULK INSERT Employee FROM 'path\tempFile.csv ' 
   WITH (FORMATFILE = 'path\tempFile.fmt');

格式文件(tempFile.fmt)如下所示:

11.0
2
1 SQLCHAR 0 50“ \ t” 2名称SQL_Latin1_General_CP1_CI_AS
2 SQLCHAR 0 50“ \ r \ n” 3地址SQL_Latin1_General_CP1_CI_AS

此处有更多详细信息-http: //msdn.microsoft.com/zh-cn/library/ms179250.aspx


这应该可行,请阅读文档,涵盖了跳过列,但没有专门介绍不跳过PK列。实际上,我必须能够做到这一点而没有错误:消息4866,级别16,状态7,第6行批量加载失败。数据文件中第1列第1列的列太长。请验证是否正确指定了字段终止符和行终止符。消息7301,级别16,状态2,第6行无法从OLE DB访问接口“ BULK”获得链接服务器“(空)”的所需接口(“ IID_IColumnsInfo”)。
sboggs11

2

我的解决方案是将ID字段添加为表中的LAST字段,因此批量插入将忽略它,并获得自动值。干净简单...

例如,如果插入到临时表中:

CREATE TABLE #TempTable 
(field1 varchar(max), field2 varchar(max), ... 
ROW_ID int IDENTITY(1,1) NOT NULL)

注意,该ROW_ID字段必须始终指定为LAST字段!


在某些情况下这可能会起作用,但是当我尝试这样做时,它只是给了我一个空白表。
布莱斯·瓦格纳

在2008R2中似乎不起作用。我得到与OP在4列上进行批量插入相同的错误,我的数据文件中包含4列,目标表中有第5个身份列。在我得到的Row 2 File Offset 528 ErrorFile Offset 0 - HRESULT 0x80020005并在stderr中的error.txt文件中:msgtext = 'Bulk load data conversion error (type mismatch or invalid character for the specified codepage) for row 2, column 5 (QMid).'
mpag

1

我遇到了同样的问题,这造成了数小时的损失,因此我很高兴分享我的发现和对我有用的解决方案。

1.使用Excel文件

这是我采用的方法。我没有使用csv文件,而是使用了具有如下内容的Excel文件(.xlsx)。

id  username   email                token website

    johndoe   johndoe@divostar.com        divostar.com
    bobstone  bobstone@divosays.com        divosays.com

请注意,id列没有值。

接下来,使用Microsoft SQL Server Management Studio连接到数据库,然后右键单击数据库并选择导入数据(任务下的子菜单)。选择Microsoft Excel作为源。当到达称为“选择源表和视图”的阶段时,单击编辑映射。对于id“目标”下的列,单击它,然后选择“忽略”。不要检查Enable Identity insert除非要从另一个数据库导入数据并且想要维护源数据库的自动增量ID,否则ID。继续完成,仅此而已。您的数据将顺利导入。

2.使用CSV文件

在您的csv文件中,确保您的数据如下所示。

id,username,email,token,website
,johndoe,johndoe@divostar.com,,divostar.com
,bobstone,bobstone@divosays.com,,divosays.com

运行以下查询:

BULK INSERT Metrics FROM 'D:\Data Management\Data\CSV2\Production Data 2004 - 2016.csv '
WITH (FIRSTROW = 2, FIELDTERMINATOR = ',', ROWTERMINATOR = '\n');

这种方法的问题是CSV应该位于数据库服务器或数据库可以访问的某些共享文件夹中,否则您可能会收到诸如“无法打开文件。操作系统返回错误代码21(设备尚未准备就绪)的错误信息。 )”。

如果要连接到远程数据库,则可以将CSV上载到该服务器上的目录,并在批量插入中引用该路径。

3.使用CSV文件和Microsoft SQL Server Management Studio导入选项

像第一种方法一样启动您的导入数据。对于源,请选择“平面文件源”,然后浏览以查找CSV文件。确保右侧菜单(常规,列,高级,预览)正常。确保在“列”菜单(列定界符)下设置正确的定界符。就像上面的excel方法一样,点击编辑映射。对于“目标”下的“ id”列,单击它并选择“忽略”

继续完成,仅此而已。您的数据将顺利导入。


1
  1. 用标识列+其他列创建一个表;
  2. 在其上创建一个视图,并仅显示要批量插入的列;
  3. 视图中的BCP

2
欢迎使用堆栈溢出。最好的答案包括更多有关您的答案如何有用以及可能与您的答案有何不同的解释。
CGritton

1

这是一个很老的答案,但是给出的答案都不能在不改变条件的情况下解决问题,而我不能这样做。

我通过使用BULK INSERT的OPENROWSET变体解决了它。这使用相同的格式文件并以相同的方式工作,但是它允许使用SELECT语句读取数据文件。

创建表:

CREATE TABLE target_table(
id bigint IDENTITY(1,1),
col1 varchar(256) NULL,
col2 varchar(256) NULL,
col3 varchar(256) NULL)

打开命令窗口运行:

bcp dbname.dbo.target_table format nul -c -x -f C:\format_file.xml -t; -T

这将基于表的外观创建格式文件。

现在,编辑格式文件,并删除FIELD ID =“ 1”和COLUMN SOURCE =“ 1”的整行,因为这在我们的数据文件中不存在。
还要根据数据文件的需要调整终止符:

<?xml version="1.0"?>
<BCPFORMAT xmlns="http://schemas.microsoft.com/sqlserver/2004/bulkload/format" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <RECORD>
  <FIELD ID="2" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="256" COLLATION="Finnish_Swedish_CI_AS"/>
  <FIELD ID="3" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="256" COLLATION="Finnish_Swedish_CI_AS"/>
  <FIELD ID="4" xsi:type="CharTerm" TERMINATOR="\r\n" MAX_LENGTH="256" COLLATION="Finnish_Swedish_CI_AS"/>
 </RECORD>
 <ROW>
  <COLUMN SOURCE="2" NAME="col1" xsi:type="SQLVARYCHAR"/>
  <COLUMN SOURCE="3" NAME="col2" xsi:type="SQLVARYCHAR"/>
  <COLUMN SOURCE="4" NAME="col3" xsi:type="SQLVARYCHAR"/>
 </ROW>
</BCPFORMAT>

现在,我们可以通过选择将数据文件批量加载到表中,从而完全控制列,在这种情况下,无需将数据插入到identity列中:

INSERT INTO target_table (col1,col2, col3)
SELECT * FROM  openrowset(
bulk 'C:\data_file.txt',
formatfile='C:\format_file.xml') as t;

0

如果使用的是临时表而不是临时表,则另一种选择可能是按照导入期望创建临时表,然后在导入后添加标识列。

因此,您的sql会执行以下操作:

  1. 如果存在临时表,则删除
  2. 创建临时表
  3. 批量导入到临时表
  4. 更改临时表添加身份
  5. <您想对数据做什么>
  6. 放置温度表

仍然不是很干净,但这是另一种选择……为了安全起见,可能还必须获得锁。

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.