Search code examples
sqlsql-servert-sqlsql-merge

Tables joins to get result


This is the table that needs to be updated.

TABLE A
-------------------
ID   UserID   Value
-------------------
1    1        1A
2    1        1B
3    1        1C
4    2        2A
5    3        3A
6    4        4A

I have a temp table that contains the new values for users that were updated.

TEMP TABLE
-------------
UserID  Value
-------------
1   1A         --existing
1   1D         --new
2   2B         --new

I'd like to know how I can update TABLE A to reflect the new values in the TEMP TABLE. The expected outcome would be:

TABLE A
-------------------
ID   UserID   Value
-------------------
1    1        1A
7    1        1D
8    2        2B
5    3        3A
6    4        4A

Two ideas I have are:

  • Delete then Insert. Determine what values no longer exist using a left join then delete them. Then determine what values are new using a right join then insert them.
  • Maybe I can use MERGE, however I quite sure how to implement it.

Test Environment

IF OBJECT_ID('tempdb..#tableA') IS NOT NULL
BEGIN
    DROP TABLE #tableA
END

IF OBJECT_ID('tempdb..#tempTable') IS NOT NULL
BEGIN
    DROP TABLE #tempTable
END

CREATE TABLE #tableA
(
    ID int identity(1,1),
    UserID int,
    Value nvarchar(50)
)

CREATE TABLE #tempTable
(
    UserID int,
    Value nvarchar(50)
)

INSERT INTO #tableA([UserID], [Value])
VALUES (1, '1A'),
(1, '1B'),
(1, '1C'),
(2, '2A'),
(3, '3A'),
(4, '4A')

INSERT INTO #tempTable([UserID], [Value])
VALUES (1, '1A'),
(1, '1D'),
(2, '2B')

SELECT * FROM #tableA
SELECT * FROM #tempTable

Edit: The following solution deletes ID (1,2,3,4) from TABLE A however I only want it to delete (2,3,4). This is because ID 1 already exists in TABLE A and does not need to be deleted and inserted again.

Delete 
From    TableA  A
Where Exists
(
    Select  *
    From    TempTable   T
    Where   T.UserId = A.UserId
)

I've come with a solution however I find it quite messy. Is there a way to make it better?

-- This will get the IDS (1,2,3,4) from TABLE A
SELECT * INTO #temp1 FROM #tableA
WHERE EXISTS
(
    Select *
    From #tempTable T
    Where T.UserId = #tableA.UserId
)

--This will get the ID (1) from TABLE A. I do not want this deleted.
SELECT * INTO #temp2 FROM #tableA
WHERE EXISTS
(
    Select *
    From #tempTable T
    Where T.UserId = #tableA.UserId AND T.[Value]=#tableA.Value
)

--LEFT JOIN to only delete the IDS (2,3,4)
DELETE FROM #tableA
WHERE EXISTS
(
    SELECT *
    FROM #temp1 a LEFT JOIN #temp2 b
    ON a.UserID=b.UserID AND a.Value=b.Value
    WHERE b.UserID IS NULL AND b.Value IS NULL
)

Solution

  • --This will remove all records which have at least one row with a matching UserID in tempTable and which don't have a row that matches on both UserID and Value.
    DELETE
      TableA  A
    WHERE 
     A.UserID IN (SELECT DISTINCT USerID FROM TempTable)
    AND NOT EXISTS
    (
        Select  1
        From    TempTable   T
        Where   T.UserId = A.UserId
        AND     T.Value= A.Value
    )
    --This will add any rows from temptable that don't have a match already in TableA
    INSERT INTO TableA(UserId, Value)
    SELECT DISTINCT UserID,Value
    FROM TempTable T
    WHERE NOT EXISTS (SELECT 1 FROM TableA A 
    WHERE T.UserID=A.UserID
    AND T.value=A.Value)
    

    This will get the results you want. If it's a huge resultset - either a much wider table in real life or millions of rows, then there may be performance implications that would require stepping back. Otherwise, will do the trick