从 csv 文件批量插入 - 忽略有错误的行 - SQL Server

Posted

技术标签:

【中文标题】从 csv 文件批量插入 - 忽略有错误的行 - SQL Server【英文标题】:Bulk insert from csv file - Ignore rows with errors - SQL Server 【发布时间】:2017-03-30 00:09:08 【问题描述】:

我正在尝试将数据从 csv 文件导入 SQL Server。 csv 文件中有数千个条目,其中有很多行包含不正确的数据。

CSV 文件中的一些行是:

`"ID"|"EmpID"|"FName"|"LName"|"Gender"|"DateOfBirth"
"1"|"90043041961"|"ABCD"|"TEST"|"F"|"1848-05-05 00:00:00.000"
"1"|"10010161961"|"XYZ"|"TEST"|"F"|"1888-12-12 00:00:00.000"
.
.
..
..
....
"4"|"75101141821PPKKLL"|"LLKK"|"F"|"1925-09-09 00:00:00.000"|""
"4"|"32041401961UUYYTT"|"PPLL"|"M"|"1920-01-01 00:00:00.000"|""
.
.....
"25"|"00468132034"|"FGTT"|"OOOO"|"F"|"1922-11-11 00:00:00.000"
"25"|"00468132034"|"KKKK"|"PPPP"|"F"|"1922-11-11 00:00:00.000"

创建 TestTable 并尝试将数据(来自 csv 文件)插入其中:

create table TestTable
(
     ID varchar(5),
     EmpID varchar(25),
     FName varchar(25),
     LName varchar(25),
     Gender varchar(5),
     DateOfirthB varchar(30)
);

我正在使用以下脚本将数据从 csv 文件导入到 SQL Server 中的TestTable

bulk insert TestTable
from 'C:\TestData.csv'
with 
   (firstrow = 2,
    DATAFILETYPE='char',
    FIELDTERMINATOR= '"|"',
    ROWTERMINATOR = '\n',
    ERRORFILE ='C:\ImportErrors.csv',
    MAXERRORS = 0,
    TABLOCK
   );

错误:

消息 4863,第 16 级,状态 1,第 1 行 第 32763 行第 5 列(性别)的批量加载数据转换错误(截断)。

消息 4863,第 16 级,状态 1,第 1 行 第 32764 行第 5 列(性别)的批量加载数据转换错误(截断)。

有什么方法可以忽略由于某种或其他原因无法添加的行(在 csv 文件中)并插入具有正确语法的行?

谢谢

PS:我不能使用 SSIS。只允许使用 SQL

【问题讨论】:

我知道这并不能回答你的问题,但老实说,我认为你最好尝试使用 SSIS 来执行此操作,而不是使用原始 sql @DForck42 -- 我希望我可以使用 SSIS 但不能。我只能为此使用 SQL...谢谢您的建议 通常的过程是首先将您的数据导入一个更宽容的临时表(即所有字段都是 NVARCHAR(4000))。然后使用 SQL 清理数据并将其移动到最终表中。就我个人而言,我更喜欢这种方法而不是我自己的 SSIS @Nick.McDermaid -- 好的..我使用 NVARCHAR(4000) 并将数据导入临时表然后清理它...听起来不错.. 但是 csv 表中有行像"4"|"75101141821PPKKLL"|"LLKK"|"F"|"1905-05-05 00:00:00.000"|"" 我有一个额外的列条目.. 我该如何忽略这些? 你的意思是说你有一个额外的管道,所以看起来有太多的列?您将整行导入到一列中,然后再次使用 SQL 清理它并将列拆分出来。听起来很愚蠢,但它确实有效。事实上,如果您要使用 SSIS,您也会遇到同样的问题 - 如果文本驱动程序无法正确识别列,您需要导入整行并手动拆分它 【参考方案1】:

由于 OP 声明“[...] 插入具有正确语法的那个”,我想知道为什么没有人建议修改 MAXERRORS 子句。尽管并非所有错误都可以伪装,但它对于转换错误非常有效。 因此,我的建议是使用 MAXERRORS=999 代替 MAXERRORS=0(根据原始示例)。

【讨论】:

这应该是评论而不是答案,请用详细的解决方案更新您的帖子,【参考方案2】:

我每周处理从不同来源收到的不同 CSV 文件,因此其中的数据非常干净整洁,而其他数据则是一场噩梦。这就是我处理收到的 CSV 字段的方式,希望对您有所帮助。您仍然需要添加一些数据验证来处理格式错误的数据。

SET NOCOUNT ON
GO

-- Create Staging Table
    IF OBJECT_ID(N'TempDB..#ImportData', N'U') IS NOT NULL
        DROP TABLE #ImportData

    CREATE TABLE #ImportData(CSV NVARCHAR(MAX))

-- Insert the CSV Data
    BULK INSERT #ImportData
        FROM 'C:\TestData.csv' 

-- Add Control Columns
    ALTER TABLE #ImportData 
        ADD ID INT IDENTITY(1, 1)

    ALTER TABLE #ImportData 
        ADD Malformed BIT DEFAULT(0)

-- Declare Variables
    DECLARE @Deliminator NVARCHAR(5) = '|', @ID INT = 0, @DDL NVARCHAR(MAX)
    DECLARE @NumberCols INT = (SELECT LEN(CSV) - LEN(REPLACE(CSV, @Deliminator, '')) FROM  #ImportData WHERE ID = 1)

-- Flag Malformed Rows
    UPDATE #ImportData
        SET Malformed = CASE WHEN LEN(CSV) - LEN(REPLACE(CSV, @Deliminator, '')) != @NumberCols THEN 1 ELSE 0 END

-- Create Second Staging Table
    IF OBJECT_ID(N'TestTable', N'U') IS NOT NULL
        DROP TABLE TestTable

    CREATE table TestTable
        (ID varchar(4000),
        EmpID varchar(4000),
        FName varchar(4000),
        LName varchar(4000),
        Gender varchar(4000),
        DateOfirthB varchar(4000));

-- Insert CSV Rows
    WHILE(1 = 1)
        BEGIN
            SELECT TOP 1
                @ID = ID
                ,@DDL = 'INSERT INTO TestTable(ID, EmpID, FName, LName, Gender, DateOfirthB)' + CHAR(13) + CHAR(10) + REPLICATE(CHAR(9), 1)
                            + 'VALUES' -- + CHAR(13) + CHAR(10) + REPLICATE(CHAR(9), 2)
                                    + '(' + DDL + ')'
            FROM
                (
                    SELECT 
                        ID
                        ,DDL = '''' + REPLACE(REPLACE(REPLACE(CSV, '''', ''''''), @Deliminator, ''','''), '"', '')  + ''''
                    FROM  
                        #ImportData 
                    WHERE 
                        ID > 1
                        AND Malformed = 0) D
            WHERE
                ID > @ID
            ORDER BY
                ID

            IF @@ROWCOUNT = 0 BREAK

            EXEC sp_executesql @DDL
        END

-- Clean Up
    IF OBJECT_ID(N'TempDB..#ImportData', N'U') IS NOT NULL
        DROP TABLE #ImportData

-- View Results
    SELECT * FROM dbo.TestTable

【讨论】:

psst:不是@Deliminator,而是@Delimiter。但无论如何都很棒的脚本。 我故意拼错了我发布的代码中的单词,这样如果我看到它发布在其他网站上我知道它来自哪里;-),谢谢! @MarkKram -- 非常感谢

以上是关于从 csv 文件批量插入 - 忽略有错误的行 - SQL Server的主要内容,如果未能解决你的问题,请参考以下文章

从 Excel / CSV 批量插入到 SQL Server

使用 JooQ 从 CSV 中“批量插入”并同时跟踪插入的记录?

在 SQL Server 中批量插入部分引用的 CSV 文件

批量从 SQL Server 检索数据

使用 pyodbc 批量插入 SQL Server 表:找不到文件

svn怎么忽略有版本控制的本地问题?