Reputation: 811
I have table like this :
Table A:
customerId mobNo1 mobNo2 mobNo3
-----------------------------------------
123 999 997 987
124 888 666 555
Table B:
customerId mobNumbers
-------------------------
123 999
123 997
123 987
124 888
124 666
124 555
I need to write a MERGE
statement so that the data from tableA to get updated or inserted in to tableB
This is what I tried:
MERGE tableB tgt
USING (SELECT * FROM tableA) src ON src.customerId = tgt.customerId
WHEN MATCHED THEN
UPDATE
SET mobNumbers = src.mobNo1,
mobNumbers = src.mobNo2,
mobNumbers = src.mobNo3,
WHEN NOT MATCHED THEN
INSERT (customerID, mobileNumbers)
VALUES (src.customerID, src.mobNo1),
(src.customerID, src.mobNo2),
(src.customerID, src.mobNo3);
END
But this is not working - any idea how to achieve this?
Upvotes: 1
Views: 6284
Reputation: 280272
You can't say SET col = something, col = something else
whether you use MERGE
or not - each SET
directive applies to exactly one row, so mentioning the same column multiple times and expecting that to affect multiple rows differently is just not how it works.
The simplest approach IMHO doesn't involve MERGE
at all (and not just because of my long-standing bias against it or that several people agree); just delete the customers that match and then insert the new set for each customer. This doesn't have error handling, but is the cleanest path IMHO:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
-- just wipe out all existing rows for matching customers
DELETE b FROM dbo.tableB AS b
INNER JOIN dbo.tableA AS a
ON a.customerId = b.customerId;
-- re-populate the whole set for each matching customer
INSERT dbo.tableB(customerId, mobNumbers)
SELECT customerId, mobNumbers
FROM dbo.tableA AS a
UNPIVOT (mobNumbers FOR mobs IN ([mobNo1],[mobNo2],[mobNo3])) AS u
WHERE NOT EXISTS (SELECT 1 FROM dbo.tableB WHERE customerId = a.customerId);
COMMIT TRANSACTION;
Upvotes: 5
Reputation: 173
You can do without those UNION using UNPIVOT
Create tables and insert data
CREATE TABLE #TableA (
customerId INT
, mobNo1 INT
, mobNo2 INT
, mobNo3 INT )
CREATE TABLE #TableB (
customerId INT
, mobNumbers INT )
INSERT #TableA VALUES
(123, 999, 997, 988)
, (124, 888, 666, 555);
INSERT #TableB VALUES
(123, 999)
, (123, 997)
, (123, 987)
, (124, 888)
, (124, 666)
, (124, 555);
The unpivot will "join" the columns that are descripted, you can see the command alone:
SELECT * FROM #TableA
UNPIVOT (
Value
FOR Origin
IN (mobNo1, mobNo2, mobNo3)) Unp
If you do it like this, it's easier to put new columns in the query, only write the name of the column in the unpivot
Merge:
MERGE #TableB B
USING (
SELECT * FROM #TableA
UNPIVOT (
Value
FOR Origin
IN (mobNo1, mobNo2, mobNo3)) Unp
) A
ON
B.customerId = B.customerId
AND B.mobNumbers = A.Value
WHEN NOT MATCHED THEN
INSERT VALUES (customerId, Value)
--WHEN NOT MATCHED BY SOURCE THEN --This option only if you want to delete the numbers that aren't in #TableA
-- DELETE
;
Upvotes: 0
Reputation: 52107
The root of the problem is that one source row maps into 3 target rows, so you need to "massage" the source appropriately.
I haven't actually tested it, but something like this should put you on the right track:
MERGE tableB tgt
USING
(
SELECT customerId, mobNo1 mobNo FROM tableA
UNION
SELECT customerId, mobNo2 FROM tableA
UNION
SELECT customerId, mobNo3 FROM tableA
) src
ON
src.customerId = tgt.customerId
AND src.mobNumbers = tgt.mobNo
WHEN NOT MATCHED THEN
INSERT (customerID, mobileNumbers)
VALUES (src.customerID, src.mobNo);
By the way, MERGE cannot modify the same row more than once (you'll get a run-time error if you try).
Upvotes: 2