UNPIVOT перекладає стовпці в рядки. У процесі він виключає значення NULL ( посилання ).
З огляду на вхід
create table #t
(
ID int primary key,
c1 int null,
c2 int null
);
insert #t(id, c1, c2)
values
(1, 12, 13),
(2, null, 14),
(3, 15, null),
(4, null, null);
запит UNPIVOT
select
ID, ColName, ColValue
from
(
select *
from #t
) as p
unpivot
(
ColValue for ColName in
(c1, c2) -- explicit source column names required
) as unpvt;
буде давати вихід
| ID | ColName | ColValue |
|----|---------|----------|
| 1 | c1 | 12 |
| 1 | c2 | 13 |
| 2 | c2 | 14 |
| 3 | c1 | 15 |
На жаль, рядок 4 видалено повністю, оскільки він містить лише NULL! Це можна зручно повторно ввести, вводячи фіктивне значення у вихідний запит:
select
ID, ColName, ColValue
from
(
select
-5 as dummy, -- injected here, -5 is arbitrary
*
from #t
) as p
unpivot
(
ColValue for ColName in
(dummy, c1, c2) -- referenced here
) as unpvt;
Агрегуючи рядки на ID, ми можемо порахувати ненульові значення. Порівняння із загальною кількістю стовпців у вихідній таблиці дозволить визначити рядки, що містять один або більше NULL.
select
ID
from
(
select -5 as dummy, *
from #t
) as p
unpivot
(
ColValue for ColName in
(dummy, c1, c2)
) as unpvt
group by ID
having COUNT(*) <> 3;
Я обчислюю 3 як
кількість стовпців у вихідній таблиці #t
+ 1 для введеного манекена
- 1 для ідентифікатора, який не є НЕЗАДАЧОМ
Це значення можна отримати під час виконання, вивчивши таблиці каталогів.
Оригінальні рядки можна отримати, приєднавшись до результатів.
Якщо слід досліджувати значення, відмінні від NULL, вони можуть бути включені до пункту де:
...
) as unpvt
where ColValue <> '' -- will eliminate empty strings
Обговорення
Для цього потрібен ідентифікатор, який передається через UNPIVOT. Ключ був би найкращим. Якщо такої немає, її можна ввести ROW_NUMBER () , хоча це може бути дорого виконати.
Усі стовпці повинні бути чітко вказані всередині пункту UNPIVOT. Їх можна перетягнути за допомогою SSMS, як запропонував @ db2. Це не буде динамічним, коли визначення таблиці зміниться, як це було б пропозицією Аарона Бертранда. Однак це стосується майже всіх SQL.
Для мого досить обмеженого набору даних план виконання - це кластерне сканування індексу та сукупність потоків. Це буде дорожче пам'яті, ніж пряме сканування таблиці та велика кількість пропозицій АБО.