views:

668

answers:

7

I want to make sure I'm not inserting a duplicate row into my table (e.g. only primary key different). All my field allow NULLS as I've decided null to mean "all values". Because of nulls, the following statement in my stored procedure can't work:

IF EXISTS(SELECT * FROM MY_TABLE WHERE 
 MY_FIELD1 = @IN_MY_FIELD1  AND
 MY_FIELD2 = @IN_MY_FIELD2  AND
 MY_FIELD3 = @IN_MY_FIELD3  AND 
 MY_FIELD4 = @IN_MY_FIELD4  AND
 MY_FIELD5 = @IN_MY_FIELD5  AND
 MY_FIELD6 = @IN_MY_FIELD6)
 BEGIN
  goto on_duplicate
 END

since NULL = NULL is not true.

How can I check for the duplicates without having an IF IS NULL statement for every column?

+2  A: 

Use ISNULL:

ISNULL(MY_FIELD1, 'NULL') = ISNULL(@IN_MY_FIELD1, 'NULL')

You can change 'NULL' to something like 'All Values' if it makes more sense to do so.

It should be noted that with two arguments, ISNULL works the same as COALESCE, which you could use if you have a few values to test (i.e.-COALESCE(@IN_MY_FIELD1, @OtherVal, 'NULL')). COALESCE also returns after the first non-null, which means it's (marginally) faster if you expect MY_FIELD1 to be blank. However, I find ISNULL much more readable, so that's why I used it, here.

Eric
What if the value of the field is 'NULL' ?
Remus Rusanu
Use an alternative value which cannot occur, such as 'x' for a number field, or a too long string for a string field.
l0b0
@Remus: I was using a stand-in value. Obviously, it has to be a unique, unused value in the field.
Eric
A: 

You will have to use IS NULL or ISNULL. There really isn't a away around it.

William Edmondson
+4  A: 

You create a primary key on your fields and let the engine enforce the uniqueness. Doing IF EXISTS logic is incorrect anyway as is flawed with race conditions.

Remus Rusanu
Or a unique constraint, since he's indicated that there's already a primary key
bdukes
+3  A: 

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.

butterchicken
+6  A: 

Use INTERSECT operator.

It's NULL-sensitive and efficient if you have a composite index on all your fields:

IF      EXISTS
        (
        SELECT  MY_FIELD1, MY_FIELD2, MY_FIELD3, MY_FIELD4, MY_FIELD5, MY_FIELD6
        FROM    MY_TABLE
        INTERSECT
        SELECT  @IN_MY_FIELD1, @IN_MY_FIELD2, @IN_MY_FIELD3, @IN_MY_FIELD4, @IN_MY_FIELD5, @IN_MY_FIELD6
        )
BEGIN
        goto on_duplicate
END

Note that if you create a UNIQUE index on your fields, your life will be much simpler.

Quassnoi
Note INTERSECT is only available from SQLServer 2005 onwards. Some of us are few years behind :)
butterchicken
+2  A: 
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.

Shannon Severance
A: 

Did you check NULLIF? http://msdn.microsoft.com/en-us/library/ms177562.aspx

THEn