Как создать троичное условие для битового поля в T-SQL - PullRequest
3 голосов
/ 24 июля 2011

У меня есть таблица SQLExpress, которая включает битовое поле для хранения состояния TRUE / FALSE.

Что-то вроде:

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

Используя эту таблицу в качестве нашего примера, я хочу создать одну хранимую процедуру, которая будет выполнять одно из следующих действий:

  1. Получить все записи.
  2. Получить только записи с IsAlive=1.
  3. Получить только записи с IsAlive=0 or NULL.

Я пытаюсь придумать, как я могу создать свой запрос без необходимости писать условия IF / ELSE. Мне кажется, есть лучший / более чистый способ, чем сделать что-то подобное:

-- 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

Есть ли другой способ создания одинаковых результатов без необходимости создавать два разных запроса, как я делал выше?

Ответы [ 5 ]

5 голосов
/ 24 июля 2011

2 предложения о том, как это сделать:

При условии, что ваша переменная @isalive также объявлена ​​как 'bit' (что должно быть)

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

Если вы хотите использовать решение для сравнения битов, для которого @isalive не должно быть битовым (оно будет работать как для бит, так и для tinyint)

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

Второе решение - для таких ботаников, как я. Некоторым хардкорным людям это может показаться интересным (или, по крайней мере, забавным), так как я думаю, что он предлагает наилучшую возможную производительность (пожалуйста, кто-то поправит меня, если я ошибаюсь) Это мощное решение, но его трудно прочитать.

3 голосов
/ 24 июля 2011

Это будет делать то, что вы хотите:

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

На основе значения @IsAlive:

  1. Если NULL, то вернет все (потому что условие всегда верно)
  2. Если 1, то вернет эти строки с IsAlive = 1
  3. Если 0, то эти строки будут возвращены с IsAlive = 0 или NULL

COALESCE - это функция, которая возвращает свой первый аргумент, если он не равен NULL, и в этом случае он возвращает второй аргумент.

Таким образом, LHS возвращает 0, если IsAlive равен NULL, или 0 и 1, если IsAlive равен 1. RHS возвращает то же самое, когда аргумент хранимой процедуры @IsAlive равен NULL, а в противном случае просто возвращает аргумент @IsAlive.

EDIT: Предполагается, что @IsAlive является BIT. В случае с tinyint вы можете добавить оператор case:

    SELECT *
    FROM MyTable
    WHERE COALESCE(IsAlive, 0) = CASE @IsAlive
                                    WHEN 0 THEN 0
                                    WHEN 1 THEN 1
                                    ELSE COALESCE(IsAlive, 0)
                                 END
1 голос
/ 24 июля 2011

Примерно так же должно работать.

SELECT *        
FROM MyTable        
WHERE (@IsAlive = 0 and IsAlive=0)
OR (@IsAlive =1 and IsAlive =1)
OR (@IsAlive is null) 
1 голос
/ 24 июля 2011

Это не совсем точно, но довольно близко к тому, что вы можете сделать:

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
1 голос
/ 24 июля 2011

попробуйте это:

SELECT * FROM MyTable WHERE ISNULL (IsAlive, 0) = ISNULL (@IsAlive, 0)
UNION
SELECT * FROM MyTable WHERE ISNULL (@IsAlive, 0) > 1
...