T-SQL mais eficiente linha para coluna? Crosstab for XML Path, Pivot
-
26-09-2019 - |
Pergunta
Estou procurando a maneira mais performante de transformar linhas em colunas. Tenho um requisito para produzir o conteúdo do db (não esquema real abaixo, mas o conceito é semelhante) nos formatos de largura fixa e delimitados. O abaixo para a consulta XML Path me dá o resultado que eu quero, mas ao lidar com qualquer outra coisa que não sejam pequenas quantidades de dados, pode levar algum tempo.
select orderid
,REPLACE(( SELECT ' ' + CAST(ProductId as varchar)
FROM _details d
WHERE d.OrderId = o.OrderId
ORDER BY d.OrderId,d.DetailId
FOR XML PATH('')
),' ','') as Products
from _orders o
Eu olhei para o pivô, mas a maioria dos exemplos que encontrei são informações agregadas. Eu só quero combinar as linhas infantis e colocá -las no pai.
Também devo salientar que não preciso lidar com os nomes das colunas, pois a saída das linhas infantis será uma sequência de largura fixa ou uma string delimitada.
Por exemplo, dadas as seguintes tabelas:
OrderId CustomerId
----------- -----------
1 1
2 2
3 3
DetailId OrderId ProductId
----------- ----------- -----------
1 1 100
2 1 158
3 1 234
4 2 125
5 3 101
6 3 105
7 3 212
8 3 250
Para um pedido, preciso produzir:
orderid Products
----------- -----------------------
1 100 158 234
2 125
3 101 105 212 250
ou
orderid Products
----------- -----------------------
1 100|158|234
2 125
3 101|105|212|250
Pensamentos ou sugestões? Estou usando o SQL Server 2K5.
Exemplo de configuração:
create table _orders (
OrderId int identity(1,1) primary key nonclustered
,CustomerId int
)
create table _details (
DetailId int identity(1,1) primary key nonclustered
,OrderId int
,ProductId int
)
insert into _orders (CustomerId)
select 1
union select 2
union select 3
insert into _details (OrderId,ProductId)
select 1,100
union select 1,158
union select 1,234
union select 2,125
union select 3,105
union select 3,101
union select 3,212
union select 3,250
CREATE CLUSTERED INDEX IX_CL__orders on _orders(OrderId)
CREATE NONCLUSTERED INDEX IX_NCL__orders on _orders(OrderId)
INCLUDE (CustomerId)
CREATE CLUSTERED INDEX IX_CL_details on _details(OrderId)
CREATE NONCLUSTERED INDEX IX_NCL_details on _details(OrderId)
INCLUDE (DetailId,ProductId)
Usando para o caminho XML:
select orderid
,REPLACE(( SELECT ' ' + CAST(ProductId as varchar)
FROM _details d
WHERE d.OrderId = o.OrderId
ORDER BY d.OrderId,d.DetailId
FOR XML PATH('')
),' ','') as Products
from _orders o
Qual é produz o que eu quero, no entanto, é muito lento para grandes quantidades de dados. Uma das mesas infantis tem mais de 2 milhões de linhas, elevando o tempo de processamento para ~ 4 horas.
orderid Products
----------- -----------------------
1 100 158 234
2 125
3 101 105 212 250
Solução
Por definição, um pivô terá que se agregar de alguma forma, porque você pode ter várias linhas com as mesmas colunas de teclas pivô. Se você não tem várias linhas, tudo bem - mas ainda precisa escolher um operador agregado (min, max, soma).
Mas o FOR XML PATH
O construto é melhor para os valores de múltiplas linhas para a operação "pivô" de cordão único.
Não sei por que o seu não está tendo um bom desempenho. Quais índices você tem nas tabelas? Como é o seu plano de execução?