question

prasad.nyalapatla avatar image
prasad.nyalapatla asked

merge statement insert issue

iam using merge statement in my proc.target table have lineid primary key. if i get not matched rescords from source table.below error message is displayed Msg 2627, Level 14, State 1, Line 7 Violation of PRIMARY KEY constraint 'PK__t1__32489DA531F75A1E'. Cannot insert duplicate key in object 'dbo.t1'. The statement has been terminated. i given example code below create table t1(id varchar(10),ids int ,sal int,updatedDate datetime,lineId int primary key) insert into t1 select 'r1',1,10,getdate(),1 union all select 'r1',3,30,getdate(),2 union all select 'r1',4,40,getdate(),3 union all select 'r1',5,50,getdate(),4 union all select 'r2',1,100,getdate(),5 union all select 'r2',3,200,getdate(),6 ---------------- create table t2(id varchar(10),ids int ,sal int ) insert into t2 select 'r1',1,110 union all select 'r1',3,30 union all select 'r1',40,400 union all select 'r1',55,550 union all select 'r2',1,101 ------ declare @maxs int select @maxs=max(lineId) from t1 --------------------------------- --DECLARE @T TABLE(id varchar(10),ids int); MERGE t1 AS T USING t2 AS S ON t.id = s.id and t.ids = s.ids --WHEN NOT MATCHED BY TARGET -- THEN INSERT VALUES ( s.id, s.ids, s.sal,getdate()-1) WHEN MATCHED AND (t.sal != s.sal) THEN --Row exists and data is different UPDATE SET t.sal = s.sal, t.updatedDate=getdate() WHEN NOT MATCHED BY TARGET THEN INSERT(id,ids,sal,updateddate,lineid)VALUES ( s.id, s.ids, s.sal,getdate()-1,@maxs+1); how to insert primary key columns.(inserts new records based on maximan lineid+1 ) note : here i dont add identity constraint for that line id column. can you give me suitable solution
sql-server-2008merge
2 comments
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

prasad.nyalapatla avatar image prasad.nyalapatla commented ·
i have urgent requirement.....plz help me above issue
0 Likes 0 ·
Grant Fritchey avatar image Grant Fritchey ♦♦ commented ·
If any of the answers below were helpful to you, please show that by clicking on the thumbs up next to the answer. If any of the answers below solved your problem, please indicate that by clicking on the check box.
0 Likes 0 ·
Grant Fritchey avatar image
Grant Fritchey answered
The problem is really simple. You're getting more than one value returned from the match between the two tables and you're only providing a single value for the @maxs variable, @maxs+1, which will always be the same. You should look at modifying the t1 table to use an identity value for the primary key so that you can insert multiple rows. You can validate this by running this query: SELECT * FROM dbo.t1 AS t JOIN dbo.t2 AS s ON t.id = s.id AND t.ids = s.ids WHERE t.sal != s.sal; From your sample data, that returns two rows. On the second row, this query is attempting to insert the same PK value and it can't.
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Usman Butt avatar image
Usman Butt answered
I would also recommend a design level change by start using IDENTITY column. Although that would reuire creating a new table, copying all the data into the new table and then renaming of the tables, it will make sure that you would not be getting primary key error if this procedure is ran simultaneouly. Reason being, the MAX Id could be read the same by multiple users unless you are using most restricted Isolation level OR placing an exclusive LOCK to be held till the transaction commits (which is not seen in your code). But if the Design level change is not feasible/permitted, then I cannot see any other viable option but to revert to orthodox UPDATE,INSERT in two steps. Also make sure that no one else reads the same MAX Id and be able to INSERT/UPDATE the table during the execution of this code. Application Lock feature have also proved to be handy in such cases. There are workarounds like using a Derived table but that would not give you the IDs in sequence. For e.g. something like declare @maxs int select @maxs=max(lineId) from t1 --DECLARE @T TABLE(id varchar(10),ids int); MERGE t1 AS T USING (SELECT s.id, s.ids, s.sal, @maxs+ROW_NUMBER() OVER(ORDER BY (SELECT @maxs))NewLineId FROM t2 S) AS S ON t.id = s.id and t.ids = s.ids --WHEN NOT MATCHED BY TARGET -- THEN INSERT VALUES ( s.id, s.ids, s.sal,getdate()-1) WHEN MATCHED AND (t.sal != s.sal) THEN --Row exists and data is different UPDATE SET t.sal = s.sal, t.updatedDate=getdate() WHEN NOT MATCHED BY TARGET THEN INSERT(id,ids,sal,updateddate,lineid) VALUES( s.id, s.ids, s.sal,getdate()-1, NewLineId); Use of a temporary table would be another workaround but could prove to be a bad solution. So to me, if @Grant Fritchey 's excellent advice is not feasible, then break down the solution into two steps i.e. UPDATE and INSERT.
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Write an Answer

Hint: Notify or tag a user in this post by typing @username.

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.