A consulta a seguir converte uma única string de CSV compactado que representa 13 mil linhas por 2 colunas. A coluna A é um bigint. A coluna B é um inteiro pequeno.
declare
@dataCsv nvarchar(max) = '29653,36,19603,36,19604,36,29654,36'; -- abbreviated, actually 13k rows
with Input as
(
select Value,
Row = row_number() over (order by (select null)) - 1
from string_split(@dataCsv, ',') o
)
--insert into PubCache.TableName
select 78064 as CacheId,
convert(bigint, i.Value) as ObjectId,
convert(smallint, i2.Value) as BrandId
from Input i
inner hash join Input i2 -- hash to encourage string_split() only once per column
on i2.Row = i.Row + 1
where i.Row % 2 = 0
order by i.Row
Plano de Execução: https://www.brentozar.com/pastetheplan/?id=By0hYPmd6
Conforme mostrado no plano, a avaliação de convert() está ocorrendo antes da junção, então às vezes (dependendo do comprimento da entrada), ela falha com
A conversão do valor nvarchar '37645' estourou uma coluna INT2. Use uma coluna inteira maior.
Alterar temporariamente a conversão de smallint para int permite que a consulta seja concluída e a inspeção da saída da coluna BrandId mostra que ela sempre contém apenas o valor '36' para este exemplo.
Existe uma maneira simples de atrasar o convert(smallint, i2.Value) até depois da junção, para que apenas as posições CSV esperadas sejam convertidas?
Sei que existem outras maneiras de compactar um fluxo de string (como usar múltiplas variáveis ou entrelaçar diferentes caracteres divididos, etc.), mas não estou interessado em resolver este exemplo dessa forma para fins desta questão. Obrigado!
Se você quiser usar essa abordagem de auto-junção, eu usaria apenas
try_convert
para que você não dependesse de onde elaconvert
fosse avaliada.TRY_CONVERT(smallint, '37645')
retornaNULL
em vez de erro, portanto, não importa se isso acontece em uma linha posteriormente filtrada.Além disso, sua abordagem para calcular um ordinal
string_split
não é confiável .E mesmo que não fosse esse o caso,
order by (select null)
ainda assim não garantiria nada.Supondo que você não esteja no SQL Server 2022 (portanto, o
enable_ordinal
argumento não está disponível para você), você pode ajustar o CSV para colocá-lo no formato de matriz JSON e obter um ordinal dessa forma.Mas minha sugestão seria abandonar completamente a auto-junção e usar agregação condicional.
Nesse caso, se você tiver certeza de que cada segundo elemento da string será válido,
smallint
você poderá reverter paraconvert
. ACASE
expressão deve evitar que seja avaliada nos valores em queordinal %2
não é avaliada como1
.PIVOT é mais adequado para transformar uma coluna em várias colunas do que auto-junções ou agrupamentos. Com o PIVOT, você escreveria sua consulta de qualquer uma das duas maneiras a seguir.
Usando STRING_SPLIT com enable_ordinal, se disponível:
Alternativamente, usando OPENJSON, conforme apontado por @martin-smith: