在检查值是否有错误后,我需要将数据加载到最终的 SQL Server 数据库表中。这是对新加载数据的错误检查,我无法弄清楚。
我有一个存储过程(请参阅
usp_LoadSchoolCOA
)将.CSV
文件批量插入到临时表中,然后插入到最终表中,如果没有编写触发器,该表就可以工作。
但是,我需要检查插入文件中的数据是否有错误并显示发现的所有错误,并且不将数据插入最终表,或者如果没有错误,则在插入最终表之前打印未发现错误(
SchoolCOA
)-这是我想不通的地方。
我学习了如何使用 if/else if 和 try/catch 在插入存储过程中编写错误检查,但这仅适用于一次插入 1 行数据。由于批量插入,我尝试在触发器内编写错误检查(参见
trg_iu_SchoolCOA
)。但是,当我运行 usp_LoadSchoolCOA
存储过程时,我总是收到一条消息
交易在触发器中结束。该批次已中止
在阅读其他人的类似问题时,似乎触发器不应该用于运行错误检查。所以我想也许我可以在将数据合并到最终表之前使用 while 循环检查数据值错误,将它们添加到
usp_LoadSchoolCOA
存储过程中 - 但在阅读更多相关内容后,似乎 while 循环只是一个浪费CPU和其他资源。
那么以下最好的方法是什么:
我有 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 表)?
如果它应该是对表列的检查,我该如何编写以检查
NewOPEID
和 PriorOPEID
与 OPEID
表中的 SchoolDetails
列?我想我可以允许它被插入并使其成为警告,但我不知道该怎么做。
/* 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
);
你的触发器有一些致命的缺陷:
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
,否则这些将不会被强制执行。
也许您可以尝试不使用事务的INSTEAD OF触发器。因此,您可能需要在触发器中最后放置一条插入/更新语句,以便那些通过所有检查的行。
CREATE TRIGGER trg_iu_SchoolCOA
on SchoolCOA
INSTEAD OF insert, update
...