带有标识(自动增量)列的 BULK INSERT
Posted
技术标签:
【中文标题】带有标识(自动增量)列的 BULK INSERT【英文标题】:BULK INSERT with identity (auto-increment) column 【发布时间】:2012-06-06 17:47:19 【问题描述】:我正在尝试从 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 文件中 我正在寻找KEEPIDENTITY
的东西...谢谢!
其实有一个非常简单的解决方案。创建一个视图,仅省略 ID 列,然后将批量插入到视图中。
【参考方案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>
现在我们可以通过选择将数据文件批量加载到我们的表中,从而完全控制列,在这种情况下,通过不将数据插入标识列:
INSERT INTO target_table (col1,col2, col3)
SELECT * FROM openrowset(
bulk 'C:\data_file.txt',
formatfile='C:\format_file.xml') as t;
【讨论】:
【参考方案2】:您必须使用格式文件进行批量插入:
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/en-us/library/ms179250.aspx
【讨论】:
这应该可以工作,阅读文档,跳过列,但没有具体覆盖不跳过 PK 列。在实践中,我必须能够做到这一点而不会出现错误: Msg 4866, Level 16, State 7, Line 6 批量加载失败。数据文件中第 1 行第 1 列的列太长。请验证是否正确指定了字段终止符和行终止符。消息 7301,级别 16,状态 2,第 6 行无法从链接服务器“(null)”的 OLE DB 提供程序“BULK”获取所需的接口(“IID_IColumnsInfo”)。【参考方案3】:-
创建一个包含标识列 + 其他列的表;
在其上创建一个视图并仅公开您将批量插入的列;
视图中的 BCP
【讨论】:
欢迎来到 Stack Overflow。最佳答案包括更多说明您的答案有何帮助,以及您的答案与其他人的不同之处。【参考方案4】:我也遇到了同样的问题,导致我损失了几个小时,所以我很高兴能分享我的发现和对我有用的解决方案。
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 作为源。当您到达名为“选择源表和视图”的阶段时,点击edit mappings。对于目标下的id
列,单击它并选择 ignore 。不要检查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 方法一样,点击edit mappings。对于目标下的 id 列,单击它并选择 ignore 。
继续完成,就是这样。您的数据将顺利导入。
【讨论】:
【参考方案5】:我的解决方案是将 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 个标识列。在 error.txt 文件中,我得到Row 2 File Offset 528 ErrorFile Offset 0 - HRESULT 0x80020005
,在我的标准错误中:msgtext = 'Bulk load data conversion error (type mismatch or invalid character for the specified codepage) for row 2, column 5 (QMid).'
【参考方案6】:
如果您使用临时表而不是临时表,则另一种选择可能是按照导入的预期创建临时表,然后在导入后添加标识列。
所以你的 sql 做了这样的事情:
-
如果临时表存在,则删除
创建临时表
批量导入临时表
更改临时表添加身份
删除临时表
仍然不是很干净,但它是另一种选择......可能也必须获得锁以确保安全。
【讨论】:
【参考方案7】:在 csv 文件中添加一个 id 列并将其留空:
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 关键字,否则它们将被忽略,然后将使用它们而不是自动递增。
【讨论】:
即使有一个临时表,如果你想从输入文件中记录行号,你将需要这样的东西。我喜欢! 嗨@Josh Werts。到目前为止,您的解决方案对我来说是一种祝福。它适用于我的本地数据库。现在,当我使用 Microsoft SQL Server Management Studio 连接到远程数据库并运行命令时,出现错误“无法批量加载,因为无法打开文件“D:\data.csv”。操作系统错误代码 21(设备未准备好)。csv文件必须与数据库在同一台服务器上。csv文件在我的电脑上 @FokwaBest - 我想远程服务器没有你的 D: 驱动器的概念。我认为您需要创建一个远程服务器可以访问的共享文件夹,然后以这种方式引用它......类似于 \\myshare\data.csv。我不是这里的专家,并且在 sql server 中工作不多,所以如果这不起作用,也许其他人可以回答。 解决方案很好,但是如果您无法更新 csv 文件怎么办。如果您必须下载文件并将其上传到数据库中,在这种情况下我们应该怎么做? 【参考方案8】:我遇到了类似的问题,但我需要确保 ID 的顺序与源文件中的顺序一致。 我的解决方案是为 BULK INSERT 使用 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');
【讨论】:
这是迄今为止这个问题的最佳解决方案 我同意这是这样做的方法。 在与源文件不匹配的临时表中运行批量插入的最简单和最简单的方法。但是我今天才发现,显然不能保证数据将按文件顺序插入。这是大型机头/详细类型文件导入的杀手【参考方案9】:不要直接将 BULK INSERT 插入到您的真实表中。
我会一直
-
从 CSV 文件中插入 staging 表
dbo.Employee_Staging
(没有 IDENTITY
列)
可能编辑/清理/操作您导入的数据
然后使用以下 T-SQL 语句将数据复制到真实表中:
INSERT INTO dbo.Employee(Name, Address)
SELECT Name, Address
FROM dbo.Employee_Staging
【讨论】:
这个想法很好,但是您使用临时表有什么具体原因吗? @Abhi:我可以(1)删除导致悲伤的 IDENTITY 列,(2)我可以查看数据,可能删除某些行,更新一些行,然后实际导入到真实的表。 @marc_s 虽然这是个好建议,但这并不能回答问题。 jwerts 真的应该采取最佳答案。 很好的一般建议。但是即使有一个临时表,如果你想要输入文件中的行号记录怎么办? > 不要直接将 BULK INSERT 插入到您的真实表中。 - 此建议仅对 MSSQL DB 有效,例如,没有此类限制。 Postgres以上是关于带有标识(自动增量)列的 BULK INSERT的主要内容,如果未能解决你的问题,请参考以下文章
方镁石。插入后如何获取自动增量主键的值,除了last_insert_rowid()?