当一行的字段计数不正确时,您可以取消所有 VARCHAR 的 BULK INSERT 吗?

Can you cancel a BULK INSERT of all VARCHARs when a line's field count is incorrect?

我正在使用 BULK INSERT 将带分隔符的 .txt 文件加载到具有 5 列的暂存 table 中。 .txt 文件有时可能包含错误,并且每行的字段数 more/less 多于 5 个。如果出现这种情况,是否可以检测到并取消整个BULK INSERT?

每个 table 列都是 VARCHAR 类型。这样做是因为 header (H01) 和行(L0101、L0102 等)行包含不同类型的字段。因此,设置 MAXERRORS = 0 似乎不起作用,因为从技术上讲没有语法错误。结果事务被提交,catch 块永远不会激活,回滚也不会发生。行仍然被插入到 table 中错误地移动或聚集。

Expected .txt file format:

H01|Order|Date|Name|Address
L0101|Order|Part|SKU|Qty
L0102|Order|Part||Qty            <-- Fields can be blank
L0103|Order|Part|SKU|Qty

Incorrect .txt file example:

H01|Order|Date|Name|Address
L0101|Order||Part|SKU|Qty        <-- Extra field in the middle
||L0102|Order|Part|SKU|Qty       <-- Extra fields at the beginning
L0103|Order|Part|SKU|Qty||       <-- Extra fields at the end

代码:

CREATE TABLE #TempStage (

     Column1    VARCHAR(255) NULL
    ,Column2    VARCHAR(255) NULL
    ,Column3    VARCHAR(255) NULL
    ,Column4    VARCHAR(255) NULL
    ,Column5    VARCHAR(255) NULL
)

DECLARE 
     @dir           SYSNAME
    ,@fname         SYSNAME
    ,@SQL_BULK      VARCHAR(255)

SELECT 
     @dir           =   '\sharedfolder\'
    ,@fname         =   'testOrder.txt'


SET @SQL_BULK = 
        'BULK INSERT #TempStage FROM ''' + @dir + @fname + ''' WITH
        (
            FIRSTROW = 1,
            DATAFILETYPE=''char'',
            FIELDTERMINATOR = ''|'',
            ROWTERMINATOR = ''0x0a'',
            KEEPNULLS,
            MAXERRORS = 0
        )'

BEGIN TRY

    BEGIN TRANSACTION

        EXEC (@SQL_BULK)

    COMMIT TRANSACTION

END TRY

BEGIN CATCH

    ROLLBACK TRANSACTION

END CATCH

SELECT * FROM #TempStage
DROP TABLE #TempStage

预期输出:

Column1 Column2 Column3 Column4 Column5
H01 Order Date Name Address
L0101 Order Part SKU Qty
L0102 Order Part NULL Qty
L0103 Order Part SKU Qty

输出不正确,想取消以免发生这种情况(\ = 管道):

Column1 Column2 Column3 Column4 Column5
H01 Order Date Name Address
L0101 Order NULL Part SKU \ Qty
NULL NULL L0102 Order Part \ SKU \ QTY
L0103 Order Part SKU Qty\ |

SQL 服务器 2016,13.0.1742.0

正如之前许多人指出的那样:BULK INSERT 速度很快,但是 not very flexible,尤其是对于列不一致。

当您的输入可能包含错误数据时(从技术上讲,从 SQL 的角度来看,这就是您所描述的),您必须采用一种或多种不同的方法:

  1. 首先使用外部程序预处理和“清理”数据,或者
  2. BULK INSERT 到具有一个大 VARCHAR(MAX) 列的暂存 table,然后使用 SQL 自行解析和清理数据,然后使用您的方法将其移入 tables真正的列,或
  3. 使用 CLR code/tricks 有效地实现上面的 (1) and/or (2),或
  4. 写一个外部程序同时clean/pre-process和SqlBulkCopy数据到你的SQL服务器(代替BULK INSERT),或者
  5. 改为使用 SSMS(虽然仍然很难处理 bad/variable 列)

我在我的职业生涯中曾经或多次做过所有这些,它们都有些困难和耗时(工作很耗时,他们的运行-时间很不错)。

我已经创建了一种基本方法来执行我需要的操作:

加载暂存后 table,检查“|”的任何实例(或您使用的任何定界符),并在找到时引发错误。

IF EXISTS(SELECT * FROM #TempStage WHERE
        Column1 LIKE '%|%'
        OR Column2 LIKE '%|%'
        OR Column3 LIKE '%|%'
        OR Column4 LIKE '%|%'
        OR Column5 LIKE '%|%'
)
RAISERROR('Incorrect file formatting; Pipe character found.', 16, 1);