Sempre pensei que a função CONCAT fosse na verdade um wrapper sobre o + (Concatenação de Strings) com algumas verificações adicionais para facilitar nossa vida.
Não encontrei nenhum detalhe interno sobre como as funções são implementadas. Quanto ao desempenho, parece que há sobrecarga para chamar CONCAT
quando os dados estão concatenando em um loop (o que parece normal, pois há identificadores adicionais para NULLs).
Há poucos dias, um desenvolvedor modificou algum código de concatenação de strings (de +
para CONCAT)
porque não gostou da sintaxe e me disse que ficou mais rápido.
Para verificar o caso, usei o seguinte código:
DECLARE @V1 NVARCHAR(MAX)
,@V2 NVARCHAR(MAX)
,@V3 NVARCHAR(MAX);
DECLARE @R NVARCHAR(MAX);
SELECT @V1 = REPLICATE(CAST('V1' AS NVARCHAR(MAX)), 50000000)
,@V2 = REPLICATE(CAST('V2' AS NVARCHAR(MAX)), 50000000)
,@V3 = REPLICATE(CAST('V3' AS NVARCHAR(MAX)), 50000000);
onde esta é a variante um:
SELECT @R = CAST('' AS NVARCHAR(MAX)) + '{some small text}' + ISNULL(@V1, '{}') + ISNULL(@V2, '{}') + ISNULL(@V3, '{}');
SELECT LEN(@R); -- 1200000017
e esta é a variante dois:
SELECT @R = CONCAT('{some small text}',ISNULL(@V1, '{}'), ISNULL(@V2, '{}'), ISNULL(@V3, '{}'))
SELECT LEN(@R); -- 1200000017
Para strings menores, não há diferenças. Em algum momento, a CONCAT
variante se torna mais rápida:
Eu me pergunto se alguém pode compartilhar alguns detalhes internos ou explicar o comportamento, pois parece que pode haver uma regra de que é melhor concatenar strings grandes usando CONCAT
.
Versão:
Microsoft SQL Server 2022 (RTM-CU8) (KB5029666) - 16.0.4075.1 (X64) 23 de agosto de 2023 14:04:50 Copyright (C) 2022 Microsoft Corporation Standard Edition (64 bits) no Windows Server 2019 Standard 10.0 (Build 17763 : ) (Hipervisor)
O script exato é semelhante ao seguinte:
DECLARE @V1 NVARCHAR(MAX)
,@V2 NVARCHAR(MAX)
,@V3 NVARCHAR(MAX);
DECLARE @R NVARCHAR(MAX);
SELECT @V1 = REPLICATE(CAST('V1' AS NVARCHAR(MAX)), 50000000)
,@V2 = REPLICATE(CAST('V2' AS NVARCHAR(MAX)), 50000000)
,@V3 = REPLICATE(CAST('V3' AS NVARCHAR(MAX)), 50000000);
--SELECT @R = CAST('' AS NVARCHAR(MAX)) + '{some small text}' + ISNULL(@V1, '{}') + ISNULL(@V2, '{}') + ISNULL(@V3, '{}'); -- 00:00:45 -- 00:01:22 -- 00:01:20
--SELECT LEN(@R); -- 300000017
SELECT @R = CONCAT('{some small text}',ISNULL(@V1, '{}'), ISNULL(@V2, '{}'), ISNULL(@V3, '{}')) -- 00:00:11 -- 00:00:16 -- 00:00:10
SELECT LEN(@R); -- 300000017
onde estou alterando o último argumento das funções REPLICATE para gerar strings maiores para a concatenação. Então, estou executando cada variante três vezes.
Parece que sim. Observando as páginas alocadas e desalocadas
tempdb
para armazenamento desses dados LOB, parece que cada instância acaba+
criando uma nova string e alocando e desalocando páginas para elatempdb
(das quais apenas a final é necessária).Considerando que
CONCAT
apenas usa as páginas necessárias para o tamanho do resultado final e não se degrada à medida que mais+
são adicionadas, alocando páginas para (e escrevendo) strings intermediárias desnecessárias.O comprimento dos dados do resultado final é 800000034 bytes (para meu código abaixo, que inclui uma
@V4
concatenação extra). OCONCAT
método alocou 99.696 páginas. Uma média de 8.024,4 bytes por página. O SQL Server usa páginas de 8 KB, mas é necessário algum espaço para sobrecarga do cabeçalho da página.Pois
+
você pode ver como ele chega a esse número tentando as expressões intermediárias como abaixo (cada etapa usa um número razoável de páginas adicionais para o comprimento dos dados de seu resultado, mas isso é igual ao uso da página das etapas anteriores, não em vez disso de ).Como David Browne - Microsoft diz nos comentários...
No script abaixo, não atribuo à
@R
variável, pois isso exige que as páginas sejam alocadas e estou apenas focando nas páginas usadas pelos métodos de concatenação.