问题
I'm taking a bunch of CRUD opertations and creating merge storedprocs off the CUD. My stored proc looks like this
CREATE PROCEDURE usp_AdministrationHistoryMerge
@AdministrationHistoryID int out,
@AdministratorID int,
@DateCreated datetime,
@CreatedBy nvarchar(50),
@ModifiedBy nvarchar(50),
@Action int
AS
SET NOCOUNT OFF
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
DECLARE @ERROR_SEVERITY int,
@MESSAGE varchar(1000),
@ERROR_NUMBER int,
@ERROR_PROCEDURE nvarchar(200),
@ERROR_LINE int,
@ERROR_MESSAGE nvarchar(4000),
@IsActive bit,
@DateModified datetime;
begin try
if @Action = 1
begin
set @IsActive = 1
set @AdministrationHistoryID = SCOPE_IDENTITY()
end
merge [AdministrationHistory] as target
using (select @AdministratorID, @DateCreated, @CreatedBy, @DateModified, @ModifiedBy, @IsActive)
as source (AdministratorID, DateCreated, CreatedBy, DateModified, ModifiedBy, IsActive)
on (target.AdministrationHistoryID = source.AdministrationHistoryID)
when matched and @Action = -1 then
update
set IsActive = 0
when matched and @Action = 0 then
update
set ModifiedBy = @ModifiedBy,
DateModified = GETDATE()
when matched and @Action = 1 then
insert
(AdministratorID, DateCreated, CreatedBy, IsActive)
values
(@AdministratorID, @DateCreated, @CreatedBy, @IsActive);
end try
BEGIN CATCH
SET @ERROR_SEVERITY = ISNULL(ERROR_SEVERITY(),'');
SET @ERROR_NUMBER = ISNULL(ERROR_NUMBER(),'');
SET @ERROR_PROCEDURE = ISNULL(ERROR_PROCEDURE(),'');
SET @ERROR_LINE = ISNULL(ERROR_LINE(),'');
SET @ERROR_MESSAGE = ISNULL(ERROR_MESSAGE(),'');
-- Test if the transaction is uncommittable.
IF (XACT_STATE()) = -1
BEGIN
--PRINT N'The transaction is in an uncommittable state. Rolling back transaction.'
ROLLBACK TRANSACTION;
END;
-- Test if the transaction is active and valid.
IF (XACT_STATE()) = 1
BEGIN
--PRINT N'The transaction is committable. Committing transaction.'
COMMIT TRANSACTION;
END;
SET @MESSAGE = 'Error Occured in Stored Procedure ' + cast(@ERROR_PROCEDURE as varchar(200)) +
'; Line Number ' + cast(@ERROR_LINE as varchar) +
'; Message: [' + cast(@ERROR_NUMBER as varchar) + '] - '
+ cast(@ERROR_MESSAGE as varchar(255))
RAISERROR(@MESSAGE, @ERROR_SEVERITY, 1);
END CATCH;
When I go to execute this I am getting this full error
Msg 10714, Level 15, State 1, Procedure usp_AdministrationHistoryMerge, Line 36 An action of type 'WHEN MATCHED' cannot appear more than once in a 'UPDATE' clause of a MERGE statement.
I have looked around on SO and found a couple ways to resolve this, but what I have found aren't suitable solutions for this error, as instead of a delete and I need to update the record's IsActive to a 0.
Also, in my searching no one really explains why this error is being thrown, yes I know its obvious because the error is right there, but why is this not allowed to happen? and based on this circumstance are there any idea's on how to accomplish this? or should I have this merge call another storedproc when @Action is 0?
回答1:
In your MERGE statement, you have three WHEN MATCHED clauses
- Two with an
UPDATEstatement - One with an
INSERTstatement.
However, that is not allowed. It is clearly stated in the Documentation on MERGE:
The
MERGEstatement can have at most twoWHEN MATCHEDclauses.
And
If there are two
WHEN MATCHEDclauses, then one must specify anUPDATEaction and one must specify aDELETEaction.
Also important to know is:
If
UPDATEis specified in the <merge_matched> clause, and more than one row of <table_source> matches a row in target_table based on <merge_search_condition>, SQL Server returns an error. TheMERGEstatement cannot update the same row more than once, or update and delete the same row.
回答2:
The SQL standard (and e.g. Db2) allows this, but not SQL Server. This article documents how you can emulate several WHEN MATCHED THEN UPDATE clauses also in SQL Server. In your case
MERGE INTO [AdministrationHistory] AS target
USING (
SELECT @AdministratorID, @DateCreated, @CreatedBy, @DateModified, @ModifiedBy, @IsActive
) AS source (AdministratorID, DateCreated, CreatedBy, DateModified, ModifiedBy, IsActive)
ON (target.AdministrationHistoryID = source.AdministrationHistoryID)
-- Combine both predicates using OR. This is optional.
WHEN MATCHED AND @Action = -1 OR @Action = 0 THEN UPDATE SET
-- UPDATE clause from the first "WHEN MATCHED AND @Action = -1" clause
IsActive = CASE WHEN @Action = -1 THEN 0 ELSE IsActive END,
-- UPDATE clauses from the second "WHEN MATCHED AND @Action = 0" clause
ModifiedBy = CASE WHEN @Action = 0 THEN @ModifiedBy ELSE ModifiedBy END,
DateModified = CASE WHEN @Action = 0 THEN GETDATE() ELSE DateModified END
WHEN NOT MATCHED AND @Action = 1 THEN INSERT
(AdministratorID, DateCreated, CreatedBy, IsActive)
VALUES
(@AdministratorID, @DateCreated, @CreatedBy, @IsActive);
来源:https://stackoverflow.com/questions/34977599/why-when-matched-cannot-appear-more-than-once-in-a-update-clause-of-a-merge-s