Dada esta tabela:
CREATE TABLE test (
id INT NOT NULL,
description NVARCHAR(100) COLLATE Modern_Spanish_CI_AS NOT NULL
);
INSERT INTO test (id, description) VALUES (1, 'CO2');
Percebi que não consigo corrigir um problema tipográfico:
SELECT * FROM test WHERE id = 1;
UPDATE test SET description = 'CO₂' WHERE id = 1;
SELECT * FROM test WHERE id = 1;
porque a atualização corresponde, mas não tem efeito:
id description
----------- -----------
1 CO2
(1 affected rows)
(1 affected rows)
id description
----------- -----------
1 CO2
(1 affected rows)
É como se o SQL Server determinasse que, como ₂ obviamente é apenas um minúsculo 2 , o valor final não será alterado, portanto não vale a pena alterá-lo.
Alguém poderia lançar alguma luz sobre isso e talvez sugerir uma solução alternativa (além de atualizar para um valor intermediário)?
O subscrito 2 não faz parte do conjunto de caracteres varchar (em qualquer agrupamento, não apenas Modern_Spanish). Então, faça uma constante nvarchar:
@gbn já explicou o motivo básico e a correção, mas o motivo específico para o comportamento que você está vendo é este:
VARCHAR
literal (semN
prefixo) em vez de umNVARCHAR
literal (string comN
prefixo), portanto, o caractere Unicode será convertido emVARCHAR
.VARCHAR
é uma codificação de 8 bits que é, na maioria dos casos, um byte por caractere, mas também pode ser dois bytes por caractere. Por outro lado,NVARCHAR
é uma codificação de 16 bits (UTF-16 Little Endian) que tem dois bytes ou quatro bytes por caractere.VARCHAR
data é de até 256 caracteres para conjuntos de caracteres de byte único (a maioria deles) e até 65.536 caracteres para conjuntos de caracteres de byte duplo (apenas alguns deles). Por outro lado,NVARCHAR
os dados podem mapear pouco mais de 1,1 milhão de caracteres Unicode (embora pouco menos de 250k atualmente mapeados).VARCHAR
dados, diferentes agrupamentos de caracteres (com base em Idioma/Cultura) estão espalhados por várias "Páginas de Código" (ou seja, conjuntos de caracteres)VARCHAR
dados (NVARCHAR
são todos os caracteres)NVARCHAR
(ou seja, Unicode / UTF-16 / todos os caracteres) paraVARCHAR
(conjunto de caracteres com base na página de código que é especificada na maioria dos agrupamentos), o agrupamento padrão do banco de dados é usado?
).Então, o que você está vendo é uma conversão
NVARCHAR
paraVARCHAR
devido à falta doN
prefixo na string literal. E a página de código do Collation padrão para o banco de dados não contém exatamente o mesmo caractere, mas um mapeamento de "melhor ajuste" foi encontrado, e é por isso que você está obtendo um2
em vez de um?
.Você pode ver esse efeito fazendo o seguinte teste simples:
Devoluções:
Para ser claro, SE a página de código do agrupamento padrão para o banco de dados contivesse exatamente o mesmo caractere, ela teria sido traduzida para o mesmo caractere nessa página de código. E, então, no seu caso, já que você está armazenando em uma
NVARCHAR
coluna, ele teria traduzido novamente, de volta ao caractere Unicode original. O exemplo final abaixo mostra esse comportamento.IMPORTANTE: Esteja ciente de que a conversão ocorre à medida que o literal da string está sendo interpretado, antes de ser armazenado na coluna. Isso significa que, mesmo que a coluna possa conter esse caractere, ela já terá sido convertida em outra coisa, com base no Collation padrão do banco de dados, tudo devido a deixar de fora o
N
prefixo nessa string literal. E isso é exatamente o que você está (ou estava) experimentando.Por exemplo, se o agrupamento padrão do seu banco de dados fosse um dos agrupamentos coreanos (um dos quatro conjuntos de caracteres de byte duplo), você não teria visto esse problema, pois o caractere "Subscrito 2" está disponível nesse caractere definido (Página de código 949). Tente o seguinte teste para ver (ele usa o Collation da coluna em vez do Collation padrão do banco de dados, pois é mais fácil de mostrar):
Devoluções:
Como você pode ver, o Latin1_General Collations, que usa a página de código 1252 (mesma página de código que o
Modern_Spanish
Collations usa) paraVARCHAR
dados, não tem uma correspondência exata, mas eles têm um mapeamento de "melhor ajuste" (que é o que você está vendo ). MAS, os agrupamentos coreanos, que usam a página de código 949 paraVARCHAR
dados, têm uma correspondência exata para o caractere "Subscrito 2".Para ilustrar ainda mais, podemos criar um novo banco de dados com um Collation padrão de um dos Collations coreanos e, em seguida, executar o SQL exato que está na pergunta:
Devoluções:
ATUALIZAR
Para quem estiver interessado em descobrir mais sobre o que exatamente está acontecendo aqui (ou seja, todos os detalhes sangrentos), veja a investigação em duas partes que acabei de postar:
Qual agrupamento é usado para converter NVARCHAR em VARCHAR em uma condição WHERE? (Parte A de 2: “Pato”)
Qual agrupamento é usado para converter NVARCHAR em VARCHAR em uma condição WHERE? (Parte B de 2: “Coelho”)