Переход через NULL

habr.png

Многие знают и используют встроенную функцию ISNULL (X, Y), которая заменяет первый аргумент на второй в случае, если он (первый) NULL. Менее употребима обратная встроенная функция NULLIF (X, Y), которая возвращает NULL, если первый аргумент равен второму. Комбинация этих двух функций позволяет избежать использования конструкций IF-ELSE или CASE-WHEN, что делает код компактнее. Если интересно посмотреть пару примеров — добро пожаловать под кат.
Например, вот код, который выводит 10 случайных целых чисел в диапазоне от 1 до 37, и ближайшее большее или равное выведенному числу значение, кратное 6.

SELECT
        Q.Src,
        CASE 
          WHEN Q.Src % 6 = 0 THEN Q.Src 
          ELSE Q.Src + (6 - Q.Src % 6) 
        END AS NextTimes6
FROM (
        SELECT TOP 10 
                CONVERT(INT, 1 + 37 * RAND(CHECKSUM(NEWID()))) Src
        FROM SYSOBJECTS S
) Q


От CASE…WHEN можно избавиться, проделав следующий трюк — обратив в NULL результат выражения Q.Src % 6 в случае, если остаток равен 0, зная, что результат любой операции с NULL равен NULL, и восстанавливая затем обратно NULL до 0 внешней функцией ISNULL:

SELECT
        Q.Src,
        Q.Src + ISNULL(6 - NULLIF(Q.Src % 6, 0), 0) AS NextTimes6
FROM (
        SELECT TOP 10 
                CONVERT(INT, 1 + 37 * RAND(CHECKSUM(NEWID()))) Src
        FROM SYSOBJECTS S
) Q


Еще один пример — разделение строки на две части по пробелу (например, имени из строки ИмяФамилия. Типичная проблема здесь при решении «в лоб» — падение функции LEFT при передаче ей аргумента -1 в качестве значения количества отрезаемых символов в случае, когда пробела в исходной строке не находится (CHARINDEX возвращает 0):

SELECT
        Q.Src,
        CASE
          WHEN CHARINDEX(' ', Q.Src) > 0 THEN LEFT(Q.Src, CHARINDEX(' ', Q.Src) - 1)
          ELSE Q.Src
    END AS NameOnly
FROM (
        SELECT N'Петр Иванов' AS Src
        UNION ALL
        SELECT N'Иван'
) Q


Превращается в:

SELECT
        Q.Src,
        LEFT(Q.Src, ISNULL(NULLIF(CHARINDEX(' ', Q.Src), 0) - 1, LEN(Q.Src))) AS NameOnly
FROM (
        SELECT N'Петр Иванов' AS Src
        UNION ALL
        SELECT N'Иван'
) Q


Приятного SQL-программирования!

© Habrahabr.ru