MERGE语句报错:同一行被多次更新/删除问题求助
问题分析与解决方案
错误原因
- 跨库Object_ID不唯一:SQL Server中
object_id仅在单个数据库内唯一,不同数据库的存储过程可能拥有相同的object_id。你当前的MERGE仅用Object_ID作为匹配条件,会导致不同数据库的同ID存储过程匹配到目标表的同一行,触发重复更新/插入的错误。 - 源数据存在重复行:
sys.dm_exec_procedure_stats会为存储过程的每个缓存执行计划返回一行,同一存储过程可能对应多条记录,导致源数据中出现同一(DataBaseName, Object_ID)的重复条目,MERGE时会试图多次更新同一目标行。
修正后的存储过程
ALTER PROCEDURE [dbo].[ProcedureUsageStats] AS BEGIN DECLARE @command NVARCHAR(MAX) SET @command = ' USE [?]; MERGE [Db1].[dbo].[table1] AS target USING ( SELECT db_name() AS DataBaseName, v.object_id AS Object_ID, v.name AS StroredProcName, v.create_date, v.modify_date, -- 取同一存储过程的最新执行时间 MAX(ps.last_execution_time) AS LastExecutionTime, v.type, v.type_desc FROM sys.procedures v LEFT JOIN sys.dm_exec_procedure_stats ps ON v.object_id = ps.object_id -- 限定DMV的数据库范围,避免跨库匹配 AND ps.database_id = DB_ID() -- 按存储过程唯一标识分组,确保每个存储过程只返回一行 GROUP BY db_name(), v.object_id, v.name, v.create_date, v.modify_date, v.type, v.type_desc ) AS source -- 改用联合键作为匹配条件,确保唯一匹配 ON (target.DataBaseName = source.DataBaseName AND target.Object_ID = source.Object_ID) -- 目标表无匹配行则插入 WHEN NOT MATCHED BY TARGET THEN INSERT ([DataBaseName], [Object_ID], [StroredProcName], [create_date], [modify_date], [LastExecutionTime], [type], [type_desc]) VALUES (source.DataBaseName, source.Object_ID, source.StroredProcName, source.create_date, source.modify_date, source.LastExecutionTime, source.type, source.type_desc) -- 目标表有匹配行且源数据有最新执行时间则更新 WHEN MATCHED AND source.LastExecutionTime IS NOT NULL THEN UPDATE SET target.LastExecutionTime = source.LastExecutionTime, -- 可选:同步存储过程的最新修改时间 target.modify_date = source.modify_date; ' EXEC sp_MSforeachdb @command; END;
关键修改点
- 联合匹配键:MERGE的ON条件改为
target.DataBaseName = source.DataBaseName AND target.Object_ID = source.Object_ID,确保每个目标行唯一对应单个数据库的单个存储过程。 - 源数据去重:通过
GROUP BY聚合同一存储过程的记录,用MAX(ps.last_execution_time)获取最新的执行时间,避免源数据重复。 - 限定DMV范围:在
sys.dm_exec_procedure_stats的关联条件中加入ps.database_id = DB_ID(),避免误匹配其他数据库的缓存计划。
内容的提问来源于stack exchange,提问作者Immortal
相关产品推荐
相关产品推荐

