在检查值是否有错误后,我需要将数据加载到最终的 SQL 数据库表中。这是对新加载数据的错误检查,我无法弄清楚。
我有一个存储过程(请参阅
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 表)?
如果它应该是对表字段的检查,我该如何编写它来对照 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
);
你的触发器有一些致命的缺陷:
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
,否则这些将不会被强制执行。