Производительность будет плохой, но это работает в общем:
DECLARE @mockupTable TABLE (ID INT,[Group] VARCHAR(10),Col1 VARCHAR(10),Col2 VARCHAR(10),Col3 VARCHAR(10),Col4 VARCHAR(10));
INSERT INTO @mockupTable VALUES
(1,'AAA','foo','bar',NULL,NULL)
,(2,'AAA','123','far',NULL,'baz')
,(3,'BBB',NULL,NULL,NULL,NULL)
,(4,'CCC','345','123',NULL,NULL)
,(5,'AAA',NULL,NULL,'caz',NULL);
- запрос
SELECT rw.query('for $n in * return concat(",",local-name($n))').value('.','nvarchar(max)')
FROM
(
SELECT *
FROM @mockupTable t
FOR XML PATH('row'),TYPE
) A(x)
CROSS APPLY A.x.nodes('/row') B(rw);
Идея вкратце:
Преобразуем таблицу до XML. Использование SELECT *
приведет к XML с именованными элементами. Это хук для общего c сцепления с именами столбцов.
Второе, что нужно знать: по умолчанию значения NULL будут по умолчанию опущены в XML.
Результат отражает «заполненные» столбцы.
Промежуточное значение XML выглядит следующим образом (представлены только ненулевые значения):
<row>
<ID>1</ID>
<Group>AAA</Group>
<Col1>foo</Col1>
<Col2>bar</Col2>
</row>
<row>
<ID>2</ID>
<Group>AAA</Group>
<Col1>123</Col1>
<Col2>far</Col2>
<Col4>baz</Col4>
</row>
<row>
<ID>3</ID>
<Group>BBB</Group>
</row>
<row>
<ID>4</ID>
<Group>CCC</Group>
<Col1>345</Col1>
<Col2>123</Col2>
</row>
<row>
<ID>5</ID>
<Group>AAA</Group>
<Col3>caz</Col3>
</row>
ОБНОВЛЕНИЕ Ближе к вашему ожидаемый результат
Попробуйте получить идентификатор и сгруппировать ваш результат
SELECT rw.value('(ID/text())[1]','int') ID
,rw.value('(Group/text())[1]','varchar(10)') [Group]
,rw.query('for $n in *[local-name() ne "ID" and local-name() ne "Group"] return concat(",",local-name($n))').value('.','nvarchar(max)')
FROM
(
SELECT *
FROM @mockupTable t
FOR XML PATH('row'),TYPE
) A(x)
CROSS APPLY A.x.nodes('/row') B(rw);
Результат
ID Group usedColumns
1 AAA ,Col1 ,Col2
2 AAA ,Col1 ,Col2 ,Col4
3 BBB
4 CCC ,Col1 ,Col2
5 AAA ,Col3
ОБНОВЛЕНИЕ 2 Ваш сгруппированный результат
Вы можно попробовать это, чтобы получить сгруппированный результат полностью
SELECT C.gr.value('text()[1]','varchar(100)')
,C.gr.query('for $n in row/*[local-name() ne "ID" and local-name() ne "Group"]
return <n>{local-name($n)}</n>')
.query('for $n in distinct-values(n/text())
return concat(",",$n)')
.value('.','nvarchar(max)')
FROM
(
SELECT *
FROM @mockupTable t
FOR XML PATH('row'),TYPE
) A(x)
CROSS APPLY (SELECT A.x.query('for $gr in distinct-values(/row/Group/text())
return <gr>{$gr}{/row[Group=$gr]}</gr>
')) B(gr)
CROSS APPLY B.gr.nodes('/gr') C(gr);
Результат
AAA ,Col1 ,Col2 ,Col3 ,Col4
BBB
CCC ,Col1 ,Col2