Como concatenar várias colunas em uma única linha? Por exemplo:
id name car
1 sam dodge
1 ram maserati
1 john benz
1 NULL mazda
2 kirk lexus
2 Jim rolls
1 GMC
O conjunto de resultados esperado é:
ID name car
1 sam,ram,john dodge,maserati,benz,mazda,GMC
2 kirk,jim lexus,rolls
Usando uma solução que encontrei no Stack Overflow:
SELECT * FROM (
SELECT t.id,stuff([m].query('/name').value('/', 'varchar(max)'),1,1,'') AS [SomeField_Combined1],
stuff([m].query('/car').value('/', 'varchar(max)'),1,1,'') AS [SomeField_Combined2]
FROM dbo.test t
OUTER apply(SELECT (
SELECT id, ','+name AS name
,','+car AS car
FROM test WHERE test.id=t.id
FOR XML PATH('') ,type)
AS M) A)S
GROUP BY id,somefield_combined1,somefield_combined2
Existem soluções melhores? A seleção interna vem de uma união multi-tabela cara (não o 'teste' de tabela única mostrado acima). A consulta está em um TVF in-line, então não posso usar uma tabela temporária.
Além disso, se houver uma coluna em branco, os resultados produzirão vírgulas extras como
ID name car
1 sam,ram,john,, dodge,maserati,benz,mazda,GMC
2 kirk,jim lexus,rolls
Existe alguma maneira de evitar isso?
Fiz alguns testes usando um pouco mais de 6 mil linhas. Com um índice na coluna ID.
Aqui está o que eu descobri.
Sua consulta inicial:
Este funcionou por ~ 23 minutos.
Eu corri esta versão que é a versão que aprendi pela primeira vez. De certa forma, parece que deveria demorar mais, mas não demora.
Esta versão durou pouco mais de 2 minutos.
Um agregado CLR quase certamente será a maneira mais rápida de fazer isso. Mas talvez você não queira usar um por qualquer motivo...
Você diz que a fonte para isso é uma consulta cara.
Eu materializaria isso em uma
#temp
tabela primeiro para garantir que ela seja avaliada apenas uma vez.O plano de execução que obtenho para a consulta na pergunta primeiro faz a concatenação para cada linha na consulta externa e, em seguida, remove as duplicatas por
id, SomeField_Combined1, SomeField_Combined2
.Isso é incrivelmente um desperdício. A reescrita a seguir evita isso.
No entanto, para os seguintes dados de teste (1000 ids com 2156 linhas por id para mim)
Ainda encontrei a solução de Kenneth com duas
XML PATH
chamadas muito mais rápida e com menos recursos.Para cada distinto
id
,#test
ele executa duas operações em vez de uma, mas essa operação é significativamente mais barata do que construir o XML e depois analisá-lo novamente.Como já apontado por Martin Smith, um agregado CLR é provavelmente sua melhor aposta. Novamente, armazenar seus resultados em uma tabela temporária é uma boa ideia.
Aqui está outra possível implementação T-SQL que usa UNPIVOT/PIVOT:
Ele é executado aproximadamente no mesmo tempo que a solução de Kenneth.
Tente isso
Use
Right
a função para remover a vírgula inicial em vez dexml
funçõesUse
case
instruções para evitar vírgulas para espaços em brancoNota: Aqui
Group by
também pode ser substituído pordistinct
, pois não estamos usando nenhumaaggregate
função