How to compare values which may both be null in T-SQL

后端 未结 14 1622
情书的邮戳
情书的邮戳 2020-12-23 14:20

I want to make sure I\'m not inserting a duplicate row into my table (e.g. only primary key different). All my fields allow NULLS as I\'ve decided null to mean \"all values

相关标签:
14条回答
  • 2020-12-23 14:52

    Equals comparison:

    ((f1 IS NULL AND f2 IS NULL) OR (f1 IS NOT NULL AND f2 IS NOT NULL AND f1 = f2))
    

    Not Equal To comparison: Just negate the Equals comparison above.

    NOT ((f1 IS NULL AND f2 IS NULL) OR (f1 IS NOT NULL AND f2 IS NOT NULL AND f1 = f2))
    

    Is it verbose? Yes, it is. However it's efficient since it doesn't call any function. The idea is to use short circuit in predicates to make sure the equal operator (=) is used only with non-null values, otherwise null would propagate up in the expression tree.

    0 讨论(0)
  • 2020-12-23 14:53

    Along the same lines as @Eric's answer, but without using a 'NULL' symbol.

    (Field1 = Field2) OR (ISNULL(Field1, Field2) IS NULL)
    

    This will be true only if both values are non-NULL, and equal each other, or both values are NULL

    0 讨论(0)
  • 2020-12-23 14:53

    What if you want to do a comparison for values that ARE NOT equal? Just using a "NOT" in front of the previously mentioned comparisons does not work. The best I could come up with is:

    (Field1 <> Field2) OR (NULLIF(Field1, Field2) IS NOT NULL) OR (NULLIF(Field2, Field1) IS NOT NULL)
    
    0 讨论(0)
  • 2020-12-23 14:55

    Above, @drowa shows a verbose approach that I agree with. It's good because it avoids the 3-value logic problem. Many of the other approaches provided here will fail in subtle and unexpected ways when negated because they're treating null as equivalent to false which it is not.

    However, I have a workflow that I find makes it convenient-ish, here's a regex. Given code of the form

    (leftSide <=> rightSide)
    

    regex find this:

    \(([a-zA-Z0-9_.@]+)\s*<=>\s*([a-zA-Z0-9_.@]+)\)
    

    and replace with this:

    (/*$1 <=> $2*/ ($1 IS NULL AND $2 IS NULL) OR ($1 IS NOT NULL AND $2 IS NOT NULL AND $1 = $2))
    

    So I write the (leftSide <=> rightSide) code and apply the above regex transformation to get the expanded form. It'd be nicer if MSSQL offered some kind of macro expansion so I wouldn't have to do it manually, but it doesn't.

    @Drowa's answer quoted for reference:

    Equals comparison:

    ((f1 IS NULL AND f2 IS NULL) OR (f1 IS NOT NULL AND f2 IS NOT NULL AND f1 = f2))
    

    Not Equal To comparison: Just negate the Equals comparison above.

    NOT ((f1 IS NULL AND f2 IS NULL) OR (f1 IS NOT NULL AND f2 IS NOT NULL AND f1 = f2))
    

    Is it verbose? Yes, it is. However it's efficient since it doesn't call any function. The idea is to use short circuit in predicates to make sure the equal operator (=) is used only with non-null values, otherwise null would propagate up in the expression tree.

    0 讨论(0)
  • 2020-12-23 14:58
    IF EXISTS(SELECT * FROM MY_TABLE WHERE 
                (MY_FIELD1 = @IN_MY_FIELD1 
                         or (MY_FIELD1 IS NULL and @IN_MY_FIELD1 is NULL))  AND
                (MY_FIELD2 = @IN_MY_FIELD2 
                         or (MY_FIELD2 IS NULL and @IN_MY_FIELD2 is NULL))  AND
                (MY_FIELD3 = @IN_MY_FIELD3 
                         or (MY_FIELD3 IS NULL and @IN_MY_FIELD3 is NULL))  AND
                (MY_FIELD4 = @IN_MY_FIELD4 
                         or (MY_FIELD4 IS NULL and @IN_MY_FIELD4 is NULL))  AND
                (MY_FIELD5 = @IN_MY_FIELD5 
                         or (MY_FIELD5 IS NULL and @IN_MY_FIELD5 is NULL))  AND
                (MY_FIELD6 = @IN_MY_FIELD6
                         or (MY_FIELD6 IS NULL and @IN_MY_FIELD6 is NULL)))
                BEGIN
                        goto on_duplicate
                END
    

    Wordy As compared to the IFNULL/COALESCE solution. But will work without having to think about what value will not appear in the data that can be used as the stand in for NULL.

    0 讨论(0)
  • 2020-12-23 15:06

    You could coalesce each value, but it's a bit wince-inducing:

        IF EXISTS(SELECT * FROM MY_TABLE WHERE 
        coalesce(MY_FIELD1,'MF1') = coalesce(@IN_MY_FIELD1,'MF1')  AND
        ...
        BEGIN
                goto on_duplicate
        END
    

    You'd also need to ensure that the coalesced value is not an otherwise valid value on the column in question. For example, if it was possible that the value of MY_FIELD1 could be 'MF1' then this would cause a lot of spurious hits.

    0 讨论(0)
提交回复
热议问题