)AS unpvt
Здесь COALESCE(colx,777) заменяет NULL-значения в столбце colx на 777, а функция NULLIF(col,777) выполняет обратное преобразование.
Последнее решение дает нам требуемый результат, однако содержит один изъян - значение 777 может рано или поздно появиться в данных, что будет приводить к неверным результатам. Чтобы устранить этот огрех, можно использовать значение другого типа, которого заведомо не может присутствовать в целочисленном столбце, например, символ 'x'. Естественно, чтобы применить этот подход, для совместимости типов целочисленный тип столбцов следует конвертировать к символьному типу, выполнив при необходимости обратное преобразование конечного результата:
SELECT CAST(NULLIF(col,'x') AS INT)
FROM
(SELECT COALESCE(CAST(col1 AS VARCHAR),'x') col1,
COALESCE(CAST(col2 AS VARCHAR),'x') col2
FROM T) p
UNPIVOT
(col FOR xxx IN
(col1, col2)
)AS unpvt
Несколько слов об эффективности представленных решений. Согласно плану выполнения запроса, основные затраты обусловлены чтением данных (операция сканирования таблицы - Table scan). Для двух первых решений сканирование выполняется дважды, в то время как для последнего (UNPIVOT) - один раз, чем и обусловлено его двойное преимущество в производительности.
DROP TABLE T
Комментарии
Н.Петров (aka sql chuvak) 28-08-2008
Есть еще один вариант, который я использую:
SELECT
CASE a WHEN 1 THEN col1 ELSE col2 END col
FROM T, (SELECT 1 a UNION ALL SELECT 2) B
Декартово произведение таблицы T с выборкой из 2-х строк дает "удвоение" (каждая строка таблицы повторяется 2 раза - для а=1 и а=2). Для первого случая берем значение из col1, а для второго - из col2.
Тут, конечно, есть и union, и join, но, по-моему, в данном вопросе интересует именно единственное сканирование таблицы.