Eu tenho um UDF escalar CLR implementado por meio de código C#. Percebi que usar o String
tipo de dados para parâmetros de entrada melhora significativamente o desempenho em comparação com o SqlString
tipo de dados. Em Stairway to SQLCLR Level 5: Development (Usando .NET no SQL Server) , Solomon Rutzky menciona os seguintes motivos para preferir os tipos de dados SQL para strings:
A principal diferença entre os tipos de dados CLR (Common Language Runtime) nativos e os tipos de dados do SQL Server é que os primeiros não permitem valores NULL, enquanto os últimos fornecem semântica NULL completa.
...
Os valores de streaming podem ser obtidos via SqlChars para N[VAR]CHAR, SqlBytes para [VAR]BINARY e SqlXml.CreateReader() para XML...
...
Ao usar SqlString (não string ou mesmo SqlChars) você pode acessar as propriedades CompareInfo, CultureInfo, LCID e SqlCompareOptions...
Eu sei que minha entrada nunca será NULL, não preciso transmitir os valores e nunca verificarei as propriedades de agrupamento. Meu caso poderia ser uma exceção em que é melhor usar String
em vez de SqlString
? Se eu for com essa abordagem, há algo em particular que eu deva observar?
Se for importante, estou usando o agrupamento padrão do SQL Server. Aqui está parte do meu código-fonte, s1
sendo o parâmetro de entrada:
fixed (char* chptr = s1)
{
char* cp = (char*)current;
for (int i = 0; i < s1.Length; i++)
{
cp[i] = chptr[i];
}
}
Excelente pergunta. Até onde eu sei, nessas condições (ou seja, sem garantias
NULL
e sem necessidade de funcionalidade extra), não deve haver nenhuma preocupação específica. Esta poderia ser uma situação semelhante aCURSOR
s onde, se uma regra genérica for necessária, seria: "não use cursores". Mas, a regra atual é: "só use cursores quando/onde apropriado". O problema é educar as pessoas sobre os detalhes técnicos dos cursores para que possam tomar essa decisão, ou seja, aqueles de nós que sabem o suficiente sobre essas coisas ignoram a regra genérica e passam a usá-los adequadamente.Então, eu aconselho as pessoas a "sempre" usar os
Sql*
tipos porque isso reduz a confusão e os erros. Mas, isso não quer dizer que usarstring
em sua situação não seria melhor. Eu digo vá em frente, e se você tiver algum problema comstring
, é fácil voltar e mudar paraSqlString
.Com relação ao agrupamento e sua declaração de:
Embora geralmente não importe, também não está claro o que você quer dizer aqui, pois não há um agrupamento padrão verdadeiro. É provável que você esteja se referindo ao infeliz agrupamento padrão ao instalar o SQL Server em um sistema operacional com uma configuração de idioma "inglês americano" (ou seja, LCID = 1033), que é
SQL_Latin1_General_CP1_CI_AS
. Mas ainda há três níveis de agrupamentos que podem ser todos diferentes (Instância/Servidor, Banco de Dados e Coluna), e você pode estar significando apenas um ou até dois desses níveis.A razão pela qual menciono tudo isso é que há algumas coisas não óbvias acontecendo aqui:
até certo ponto, nenhum desses 3 níveis que os agrupamentos afetam é relevante, pois a cultura padrão dos threads SQLCLR é a configuração de idioma no nível do sistema operacional (o LCID do idioma selecionado). Isso afeta as operações usando
String.Equals
ao usar um dos doisStringComparison.CurrentCulture*
valores e as operações usandoString.Compare
ao não especificar uma cultura.até certo ponto, nenhum desses 3 níveis que os agrupamentos afetam é relevante, pois o
=
operador faz uma comparação ordinal (ou seja, deve ser o mesmo que usar um_BIN2
agrupamento). É assim também queString.CompareOrdinal
funciona, assim comoString.Equals
quando não passaStringComparison.CurrentCulture*
ouStringComparison.InvariantCulture*
valores.uma instância em que o agrupamento do SQL Server é importante é ao concatenar um
SqlString
parâmetro de entrada com umstring
via+
. Nesse caso, o+
operador cria um newSqlString
para conter o valor destring
para que possa concatenar os doisSqlStrings
. O problema é que o novoSqlString
é criado com os threads atuais LCID (que é o LCID do Sistema Operacional), e então o+
operador compara os doisSqlStrings
s antes da concatenação (ou seja, verifica se eles são do "mesmo tipo"). Mas, devido aoSqlString
parâmetro de entrada ter o LCID do banco de dados (não instância ou coluna) e o parâmetro criado implicitamenteSqlString
tendo um LCID do sistema operacional, a operação obtém uma exceção informando que os "agrupamentos" não correspondem. Legal, hein?Isso, no entanto, não deve ser um problema, pois ninguém deve usar o
SqlString
valor diretamente ao desejar a string. Em vez disso, todos devem sempre usar aValue
propriedade para obter a string.Dito isto, estou curioso para saber qual teste você fez para determinar que
string
foi mais rápido. Testei uma UDF simples que aceita um únicoNVARCHAR(4000)
parâmetro de entrada, concatena uma string curta e depois retorna o novo valor. Uma versão dessa UDF aceita e retornastring
, e a outra versão aceita e retornaSqlString
. Mais de 1 milhão de iterações, astring
versão foi cerca de 200-300 milissegundos mais rápida do que aSqlString
versão, cerca de 50% do tempo, ao comparar seus tempos mais rápidos (em todas as 1 milhão de iterações, não por cada). Nos outros 50% do tempo, o ganho de desempenho foi de cerca de 100 milissegundos, mas também pode ser nenhum.Além disso, em relação ao seu código de teste: é
s1
sempre o parâmetro de entrada direta, sejastring
ouSqlString
? Se sim, você também deve testar a criação de uma string localmente e defini-las1.Value
. Significado:Além disso, algumas outras opções para possivelmente testar:
byte[]
)char[]
)