使用SQL存储过程加载批量插入到临时表然后加载到表时如何/在哪里进行数据错误检查?

问题描述 投票:0回答:1

在检查值是否有错误后,我需要将数据加载到最终的 SQL 数据库表中。这是对新加载数据的错误检查,我无法弄清楚。

我有一个存储过程(请参阅

usp_LoadSchoolCOA
)将 CSV 文件批量插入到临时表中,然后插入到最终表中,如果没有编写触发器,该表就可以工作。但是,我需要检查插入文件中的数据是否有错误,并显示发现的所有错误,并且不将数据插入最终表,或者如果没有错误,则在插入最终表之前打印未发现错误(
SchoolCOA
)--这是我想不通的地方。

我学习了如何使用 if/else if 和 try/catch 在插入存储过程中编写错误检查,但这仅适用于一次插入 1 行数据。由于批量插入,我尝试在触发器内编写错误检查(参见

trg_iu_SchoolCOA
)。但是,当我运行
usp_LoadSchoolCOA
存储过程时,我总是收到消息“事务在触发器中结束。批处理已中止。”

在阅读其他人的类似问题时,似乎触发器不应该用于运行错误检查。所以我想也许我可以在将数据合并到最终表之前使用 while 循环检查数据值错误,将它们添加到

usp_LoadSchoolCOA
存储过程中 - 但在阅读更多相关内容后,似乎 while 循环只是浪费CPU和其他资源。

那么以下最好的方法是什么:

  • 运行批量插入
  • 对值运行错误检查
    • 如果错误数 > 0,
      • 然后打印整个文件(或临时表)的所有错误并且
      • 不将任何值插入/合并到最终表中;
    • 或者如果错误数 = 0,
      • 打印数据检查已成功完成的语句,
      • 然后将值插入/合并到最终表中并打印加载的总行数 ?

我有 3 个表数据表,需要同样的过程。我以最短的为例。

以下是使用临时表的 SchoolCOA 表的表和批量插入存储过程的代码:

-- Create TempSchoolCOA & SchoolCOA Data Files 
create table dbo.TempSchoolCOA
( 
    NewOPEID char(8),
    PriorOPEID char(8),
    CodeCOA int,
    DateCOA char(8)
);
create table dbo.SchoolCOA 
(
    ChangeID int IDENTITY (1,1) NOT NULL PRIMARY KEY,
    NewOPEID char(8) NOT NULL CHECK (LEN(NewOPEID)=8),
    PriorOPEID char(8) NOT NULL CHECK (LEN(PriorOPEID)=8),
    CodeCOA int NOT NULL,
    DateCOA char(8) NOT NULL
);

-- Stored Procedure to bulk insert data file into SchoolCOA
CREATE PROCEDURE usp_LoadSchoolCOA
    @FullFilePath NVARCHAR(MAX) 
AS
BEGIN
    DECLARE @sql NVARCHAR(MAX)
    TRUNCATE TABLE dbo.TempSchoolCOA

    SET @sql = N'BULK INSERT dbo.TempSchoolCOA FROM ''' + @FullFilePath + ''' WITH (FORMAT=''CSV'', CHECK_CONSTRAINTS, FIRE_TRIGGERS, FIELDTERMINATOR='','', ROWTERMINATOR=''\n'', FIRSTROW=2);'
    SELECT  @sql
    EXEC sp_executesql @sql

    MERGE INTO dbo.SchoolCOA AS TGT
        USING
            (SELECT NewOPEID, PriorOPEID, CodeCOA, DateCOA FROM dbo.TempSchoolCOA) 
            AS SRC ON (TGT.NewOPEID = SRC.NewOPEID AND TGT.PriorOPEID = SRC.PriorOPEID)
            WHEN MATCHED THEN 
                UPDATE SET  
                    TGT.NewOPEID = SRC.NewOPEID,
                    TGT.PriorOPEID = SRC.PriorOPEID,
                    TGT.CodeCOA = SRC.CodeCOA,
                    TGT.DateCOA = SRC.DateCOA
            WHEN NOT MATCHED THEN 
                INSERT (
                    NewOPEID,
                    PriorOPEID,
                    CodeCOA,
                    DateCOA
                    )
                 VALUES (
                    SRC.NewOPEID,
                    SRC.PriorOPEID,
                    SRC.CodeCOA,
                    SRC.DateCOA
                    );  
END;

-- To run, use: EXEC usp_LoadSchoolCOA @FullFilePath = 'C:\Users\Admin\Documents\TU Grad School\Case Study\SQL Work\TestSchoolCOA3.csv'

这是错误捕获存储过程的代码:

-- Error Checks for Table Inserts
CREATE PROCEDURE usp_GetErrorInfo
AS SELECT
    ERROR_NUMBER() AS ErrorNumber,
    ERROR_SEVERITY() AS ErrorSeverity,
    ERROR_STATE() AS ErrorState,
    ERROR_PROCEDURE() AS ErrorProcedure,
    ERROR_LINE() AS ErrorLine,
    ERROR_MESSAGE() AS ErrorMessage

    IF @@TRANCOUNT > 0
        ROLLBACK TRANSACTION;
GO

以下是我在触发器中插入的 SchoolCOA 数据错误检查代码,但是当运行

usp_LoadSchoolCOA
过程时,它会导致消息“事务在触发器中结束。批次已中止。”如果它不应该是触发器,我怎样才能运行这些错误检查(最好是整个文件/TempSchoolCOA 表)?

如果它应该是对表字段的检查,我该如何编写它来对照 SchoolDetails 表中的 OPEID 字段检查 NewOPEID 和 PriorOPEID ?我想我可以允许它被插入并使其成为警告,但我不知道该怎么做。

/* Validate SchoolCOA inserts with Trigger */ 
CREATE TRIGGER trg_iu_SchoolCOA 
    on SchoolCOA 
    for insert, update 
    as BEGIN
        BEGIN TRY
            declare @NewOPEID char(8), @PriorOPEID char(8), @CodeCOA int, @DateCOA char(8)                         
            select @NewOPEID = (select NewOPEID from inserted), 
                @PriorOPEID = (select PriorOPEID from inserted),
                @CodeCOA = (select CodeCOA from inserted),
                @DateCOA = (select DateCOA from inserted)
    
            /* VALIDATE NewOPEID */     
            if @NewOPEID not like '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'
                begin
                    rollback transaction
                    print 'Error: NewOPEID invalid - must be 8 digits.'
                    return
                end     
            else if not exists (select 1 from SchoolDetails where OPEID = @NewOPEID)
                begin
                    rollback transaction
                    print 'Error: NewOPEID not found in SchoolDetails table: ' + @NewOPEID
                    return
                end

            /* VALIDATE PriorOPEID */   
            else if @PriorOPEID not like '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'
                begin
                    rollback transaction 
                    print 'Error: PriorOPEID invalid - must be 8 digits.' 
                    return
                end
            else if not exists (select 1 from SchoolDetails where OPEID = @PriorOPEID)
                begin
                    rollback transaction
                    print 'Error: PriorOPEID not found in SchoolDetails table: ' + @PriorOPEID
                    return
                end

            /* VALIDATE CodeCOA */
            else if not exists (select 1 from CodeCOARef where CodeCOA = @CodeCOA)
                begin
                    rollback transaction
                    print 'Error: CodeCOA not found in CodeCOARef table.'
                    return
                end

            print 'Inserted/updated data successfully passed error checks for SchoolCOA table.'
        END TRY

        BEGIN CATCH
            EXECUTE usp_GetErrorInfo;
        END CATCH; 
    END

作为参考,这里是学校详细信息表:

create table dbo.SchoolDetails (
    SchoolID int IDENTITY (1,1) NOT NULL PRIMARY KEY,
    OPEID char(8) NOT NULL CHECK (LEN(OPEID)=8),
    SchoolName varchar(80) NOT NULL,
    LocName varchar(80) NOT NULL,
    AddrLine1 varchar(100),
    AddrLine2 varchar(100),
    City varchar(50) NOT NULL,
    State2 char(2) NOT NULL CHECK (LEN(State2)=2),
    ZipCode char(5),MainOrLoc int NOT NULL,
    OpenStatus int NOT NULL,
    StartDate char(8),
    StartReason int,
    StopDate char(8),
    StopReason int
    );
sql sql-server bulkinsert error-checking
1个回答
0
投票

你的触发器有一些致命的缺陷:

  • 触发器结束时必须打开与开始时相同数量的事务。这就是回滚导致的虚假错误的原因。使用
    THROW
    抛出错误,而不是回滚,这将结束触发器并自动回滚事务。
  • 错误处理程序是一个非常糟糕的主意。它吞掉错误,允许代码继续运行,并将错误作为虚假结果集返回。完全删除它,并允许错误执行它们应该执行的操作:结束过程并向客户端显示一条消息。
  • 触发器需要处理多行(或零行),不能分配给单个变量。相反,只需做一件大事
    IF EXISTS
CREATE OR ALTER TRIGGER trg_iu_SchoolCOA 
ON SchoolCOA 
FOR INSERT, UPDATE

IF EXISTS (SELECT 1
    FROM inserted i
    WHERE i.NewOPEID NOT LIKE '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'
       OR NOT EXISTS (SELECT 1
        FROM SchoolDetails sd
        WHERE sd.OPEID = i.NewOPEID)
       OR i.PriorOPEID NOT LIKE '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'
       OR NOT EXISTS (SELECT 1
        FROM SchoolDetails sd
        WHERE sd.OPEID = i.PriorOPEID)
       OR NOT EXISTS (SELECT 1
        FROM CodeCOARef ccr
        WHERE ccr.CodeCOA = i.CodeCOA)
  )
    THROW 50001, N'Invalid data', 1;

话虽如此,不要做任何这样的事情。触发器不是强制执行此类约束的正确方法,可以使用普通的内置约束来实现。

你的桌子应该这样声明。

create table dbo.SchoolCOA 
(
    ChangeID int IDENTITY (1,1) NOT NULL PRIMARY KEY,
    NewOPEID char(8) NOT NULL
      CONSTRAINT NewOPEID_valid CHECK (NewOPEID LIKE '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]')
      CONSTRAINT NewOPEID_SchoolDetails FOREIGN KEY REFERENCES SchoolDetails (OPEID),
    PriorOPEID char(8) NOT NULL
       CONSTRAINT PriorOPEID_valid CHECK (PriorOPEID LIKE '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]')
       CONSTRAINT PriorOPEID_SchoolDetails FOREIGN KEY REFERENCES SchoolDetails (OPEID),
    CodeCOA int NOT NULL
       CONSTRAINT CodeCOA_ FOREIGN KEY REFERENCES CodeCOA (CodeCOARef),
    DateCOA date NOT NULL    -- dates should be in a date column, not string
);

确保在

CHECK_CONSTRAINTS
语句中设置
BULK INSERT
,否则这些将不会被强制执行。

© www.soinside.com 2019 - 2024. All rights reserved.