3

I have a SQLExpress table that includes a bit field for storing TRUE/FALSE state.

Something like:

+----+---------+
| ID | IsAlive |
+----+---------+
| 1  |    1    |
| 2  |    0    |
| 3  |   NULL  |
| 4  |    1    |
+----+---------+

Using that table as our example, I want to create one Stored Procedure that will do any one of the following:

  1. Retrieve all records.
  2. Retrieve only the records with IsAlive=1.
  3. Retrieve only the records with IsAlive=0 or NULL.

I am trying to think of how I can create my query without having to write IF/ELSE conditions - It seems to me there is a better/cleaner way than to do something like this:

-- The ternary logic...
-- 0 or NULL retrieves records where IsAlive = 0 or NULL
-- 1 retrieves records where IsAlive = 1
-- Otherwise return all records 

-- sproc .....

    @IsAlive tinyint = 2 -- Return all records by default

    AS
    BEGIN
      IF(@SentToNTService = 0 OR @SentToNTService = 1)
       BEGIN
        SELECT *
        FROM MyTable
        WHERE IsAlive = @IsAlive;
       END
    ELSE -- Lame redundancy 
       BEGIN
        SELECT *
        FROM MyTable
       END  
    END

Is there another way of creating the same results without having to create two different queries as I did above?

Jed
  • 10,649
  • 19
  • 81
  • 125
  • 1
    The comment part of your script is slightly inconsistent with your description. In particular, the comment says about retrieving records where `IsAlive = 0`, but the description says about rows where `IsAlive = 0 or NULL`. – Andriy M Jul 24 '11 at 00:03
  • @Andriy - Thanks for the eagle eye ;) .. og post has been updated. – Jed Jul 24 '11 at 00:13

5 Answers5

5

2 suggestions of how to do this:

Assuming your variable @isalive is declared as 'bit' as well (which is should be)

SELECT * FROM @t
WHERE @isalive is null or @isalive = coalesce(isalive, 0)

If you want to use a 'bit compare' solution that doesn't require @isalive to be 'bit' (it will work for bit as well as tinyint)

SELECT * FROM @t
WHERE coalesce((1-coalesce(isalive, 0)) ^ @isalive, 1) > 0

Second solution is for nerds like me. Some hardcore people may find it interesting (or at least amusing) as I think it offer the best possible performance (please, someone correct me if i am wrong). It is a powerful solution but hard to read.

Jed
  • 10,649
  • 19
  • 81
  • 125
t-clausen.dk
  • 43,517
  • 12
  • 59
  • 92
  • 1
    +1, both for the simplicity of the first solution and for the craziness of the second one. Do you know that you could use `^` instead of `-` as well? – Andriy M Jul 24 '11 at 19:27
  • @Andriy M now that you mention it. I had not considered that, if i had i surely would have used it. I imagine bit operations are slightly faster than calculation – t-clausen.dk Jul 24 '11 at 19:40
3

This will do what you want:

    SELECT *
    FROM MyTable
    WHERE COALESCE(IsAlive, 0) = COALESCE(@IsAlive, COALESCE(IsAlive, 0))

Based on the value of @IsAlive:

  1. If NULL, then will return everything (because the condition is always true)
  2. If 1, then will return those rows with IsAlive = 1
  3. If 0, then will return those rows with IsAlive = 0 or NULL

COALESCE is a function that returns it's first argument, unless it's NULL, in which case it returns its second argument.

So the LHS returns 0 if IsAlive is NULL or 0 and 1 if IsAlive is 1. The RHS returns the same when the stored procedure argument @IsAlive is NULL and just returns the @IsAlive argument otherwise.

EDIT: This assumed that @IsAlive is BIT. In the case of tinyint you can add a case statement:

    SELECT *
    FROM MyTable
    WHERE COALESCE(IsAlive, 0) = CASE @IsAlive
                                    WHEN 0 THEN 0
                                    WHEN 1 THEN 1
                                    ELSE COALESCE(IsAlive, 0)
                                 END
Petar Ivanov
  • 91,536
  • 11
  • 82
  • 95
  • +1 for the clever COALESCE solution, Petar! I like it. However, I was shooting for a solution that will return all results for any integer value that is not 1 or zero. So, for example, if the sproc caller sends a value of 555, the query will return all records. In other words, a value of 1 will only return IsAlive=true, the value zero (or NULL) will return IsAlive=false, and any other integer value will return all records. – Jed Jul 23 '11 at 23:58
  • 1
    You should probably be explicit about the fact that `@IsAlive` should be `bit` in your query, not `tinyint`, like in the OP's example script. Otherwise, you've got a clever condition there, even if it's not sargable. – Andriy M Jul 24 '11 at 00:05
  • @Andriy M - indeed, '@IsAlive' should be bit, even the name surgest it. – t-clausen.dk Jul 24 '11 at 18:47
1

try this:

SELECT * FROM MyTable WHERE ISNULL (IsAlive, 0) = ISNULL (@IsAlive, 0)
UNION
SELECT * FROM MyTable WHERE ISNULL (@IsAlive, 0) > 1
Yahia
  • 69,653
  • 9
  • 115
  • 144
1

This isnt exact, but pretty close to what you can do:

SELECT *        
FROM MyTable        
WHERE CASE @IsAlive
      WHEN 0 THEN IsAlive = @IsAlive
      WHEN 1 THEN IsAlive = @IsAlive
      ELSE 1=1 --dummy true value, when null or anything else
END
Brady Moritz
  • 8,624
  • 8
  • 66
  • 100
1

Something like this should also work.

SELECT *        
FROM MyTable        
WHERE (@IsAlive = 0 and IsAlive=0)
OR (@IsAlive =1 and IsAlive =1)
OR (@IsAlive is null) 
Brady Moritz
  • 8,624
  • 8
  • 66
  • 100