We’ve all had those “facepalm” moments in coding — the kind where you stare at a line of SQL for an hour, convinced it should work. This is the story of one such moment involving a MERGE
statement in T-SQL, a sneaky nullable column, and a silent refusal to update anything.
If you’ve ever screamed internally at a query that “should work but doesn’t,” this one’s for you.
The Problem
I was trying to sync data between two tables using a MERGE
statement — standard stuff. I wanted to update rows where the Price
field had changed. My condition looked like this:
WHEN MATCHED AND target.Price <> source.Price THEN UPDATE ...
Perfectly logical, right? Wrong.
Despite clear differences in price values between the tables, my MERGE
quietly skipped rows. No errors, no warnings. Just… nothing.
It took me far too long to realize the culprit: target.Price
was nullable. And in SQL, NULL <> something
doesn’t return TRUE
— it returns UNKNOWN. And MERGE
ignores UNKNOWN
in its conditions.
The Solution
Once I remembered how SQL handles NULLs, the fix was obvious (and slightly embarrassing):
WHEN MATCHED AND ISNULL(target.Price, -1) <> source.Price THEN UPDATE …
Here’s the catch — source.Price
was not nullable, so I only needed to NULL-protect target.Price
. No need to wrap both sides in ISNULL()
— just making sure one side isn’t floating off in NULL-space was enough.
After that? It worked like a charm.
Deeper Explanation
NULLs are tricky beasts. In SQL, they represent “unknown,” and any comparison with NULL
— even NULL <> 5
— is never TRUE
, just UNKNOWN
. And MERGE
only acts on rows where the condition is explicitly TRUE
.
So if target.Price
is NULL
and source.Price
is 100.00
, SQL shrugs: “Could be different, could be the same. I dunno.” And the update gets skipped.
By using ISNULL(target.Price, -1)
, I forced the comparison to a known value. Since source.Price
was declared NOT NULL
, I didn’t need to touch it.
Best Practices
- Always consider NULLs when comparing fields in
MERGE
, especially with<>
and=
. - NULL-protect only the fields that can be NULL. Don’t overuse
ISNULL()
unnecessarily. - Pick a safe default (like
-1
) that won’t collide with real data. - Test your
MERGE
conditions with edge cases — includingNULL
s.
Conclusion
This bug was a classic case of forgetting how SQL really thinks. It wasn’t a syntax error. It wasn’t a typo. It was a logic trap — and one that quietly skipped the updates I needed.
But hey, that’s the daily grind of writing code — learning, relearning, and occasionally yelling at NULLs.
Stick around for more real-world lessons from the coding trenches. Spoiler: the bugs aren’t going anywhere.
Leave a Reply