Here is a solution based on FULL JOIN. The purpose of the subqueries with INTERSECT is to compare the columns.
EXISTS(A INSERSECT B)
is the same as A = B, but it also works when any of A and B are NULL.
; WITH CTE AS (
SELECT id = isnull(trg.id, src.id),
action = CASE WHEN trg.id IS NULL THEN 'Insert'
WHEN src.id IS NULL THEN 'Delete'
ELSE 'Update'
END,
details = CASE WHEN trg.id IS NOT NULL AND
src.id IS NOT NULL
THEN CASE WHEN NOT EXISTS (SELECT trg.description
INTERSECT
SELECT src.description)
THEN 'Description '
ELSE ''
END +
CASE WHEN NOT EXISTS (SELECT trg.name
INTERSECT
SELECT src.name)
THEN 'Name'
ELSE ''
END +
CASE WHEN NOT EXISTS (SELECT trg.IsActive
INTERSECT
SELECT src.IsActive)
THEN 'IsActive '
ELSE ''
END
END
FROM sourcetbl src
FULL JOIN targettbl trg ON src.id = trg.id
)
SELECT id, action, details
FROM CTE
WHERE action IN ('insert', 'delete') OR
len(details) > 0
PS Had you posted CREATE TABLE + INSERT with sample data, I would have tested my solution.