Descobri que as consultas abaixo retornam resultados diferentes em dois bancos de dados de hiperescala do Azure que estão executando o mesmo nível de compatibilidade, as mesmas opções de conjunto etc.
IF CAST(ServerProperty('Edition') AS nvarchar(128)) = 'SQL Azure' BEGIN
SELECT 1
END
IF ServerProperty('Edition') = 'SQL Azure' BEGIN
SELECT 2
END
IF 'SQL Azure' = ServerProperty('Edition') BEGIN
SELECT 3
END
Em um banco de dados, ele retorna apenas 1, em outro banco de dados retorna 1,2 e 3.
Eu investiguei a causa raiz e parece ser causada por diferentes agrupamentos dos bancos de dados.
Para as seguintes consultas:
SELECT SQL_VARIANT_PROPERTY(ServerProperty('Edition'), 'Collation')
SELECT name, collation_name, compatibility_level FROM sys.databases
O banco de dados que retorna apenas uma linha, o resultado é:
-----------------------------
SQL_Latin1_General_CP1_CI_AS
name collation_name compatibility_level
------------ -------------------------------- -------------------
master SQL_Latin1_General_CP1_CI_AS 140
my_database SQL_Latin1_General_CP850_CI_AS 150
E o resultado do banco de dados que retorna 1,2,3 é:
-----------------------------
SQL_Latin1_General_CP1_CI_AS
name collation_name compatibility_level
------------ -------------------------------- -------------------
master SQL_Latin1_General_CP1_CI_AS 140
my_database SQL_Latin1_General_CP1_CI_AS 150
Portanto, a comparação simples sem o elenco está comparando sql_variant
com varchar
(não há diferença quando eu uso N'SQL Azure'
) onde o subjacente nvarchar
do sql_variant
tem em um caso um agrupamento diferente do banco de dados que eu consulto e, em outro caso, é correspondente.
Em primeiro lugar, eu diria que a comparação de duas strings com agrupamento diferente falharia como falha quando você tenta unir duas colunas com agrupamento diferente, mas aparentemente não é o caso aqui.
De qualquer forma, qual é a melhor maneira de comparar com segurança a saída de uma função que pode estar sql_variant
com um varchar
?
O
sql_variant
tipo está no topo da hierarquia de precedência de dados, portanto, o literal de cadeia de caracteres é convertido implicitamentesql_variant
antes que a comparação ocorra.As regras para
sql_variant
comparação incluem (grifo nosso):Isso explica os resultados que você viu.
Além disso :
As regras para precedência de agrupamento são separadas. Eles se aplicam ao comparar strings com diferentes rótulos de agrupamento. Consulte a documentação vinculada para obter detalhes detalhados dos rótulos Coercible-default , Implicit X , Explicit X e No-collation .
Converta o
sql_variant
valor de retorno para o tipo base com o agrupamento do banco de dados atual. Isso corresponderá ao agrupamento atribuído ao valor literal, que deve ter o prefixo N para ser interpretado como Unicode.Para o meu caso, isso significa:
A
COLLATE
cláusula não é estritamente necessária se você fornecer a string literal exatamente como esperado, mas você pediu a 'melhor' maneira, que eu entendi como 'mais abrangente'.Meu colega tcheco Tomáš Zíka escreveu sobre isso e assuntos relacionados em Expecting Subvertations .
Para
SERVERPROPERTY
esse retornonvarchar
, sugiro a solução alternativa que você já descobriu mais umanvarchar
constante em vez devarchar
.A conversão explícita 1) retornará um
nvarchar
do agrupamento de banco de dados atual e 2) evitará a conversão implícita da constante varchar para sql_variant. Anvarchar
constante também será o agrupamento de banco de dados atual e corresponderá ao tipo de conversão explícito, permitindo uma comparação de igualdade mais confiável.