@gbn ha già spiegato il motivo e la correzione di base, ma il motivo specifico del comportamento che stai riscontrando è questo:
- Stai usando un valore
VARCHAR
letterale (senza N
prefisso) anziché NVARCHAR
letterale (stringa con N
prefisso), quindi il carattere Unicode verrà convertito in VARCHAR
.
VARCHAR
è una codifica a 8 bit che è, nella maggior parte dei casi, un byte per carattere, ma può anche essere due byte per carattere. D'altra parte, NVARCHAR
è una codifica a 16 bit (UTF-16 Little Endian) che è di due byte o quattro byte per carattere.
- A causa della differenza nel numero di byte disponibili da utilizzare per la mappatura dei caratteri, le codifiche a 8 bit sono, per loro stessa natura, molto più limitate nel numero di caratteri che è possibile mappare.
VARCHAR
i dati possono contenere fino a 256 caratteri per set di caratteri a byte singolo (la maggior parte di essi) e fino a 65.536 caratteri per set di caratteri a byte doppio (solo alcuni di questi). D'altra parte, i NVARCHAR
dati possono mappare poco più di 1,1 milioni di caratteri Unicode (sebbene poco meno di 250k attualmente mappati).
- A causa del numero limitato di mappature che possono essere eseguite con 8 bit /
VARCHAR
dati, diversi raggruppamenti di caratteri (basati su Lingua / Cultura) sono distribuiti su più "Pagine di codice" (ovvero set di caratteri)
- Ogni fascicolo specifica quale codice pagina, se presente, utilizzare per i
VARCHAR
dati ( NVARCHAR
sono tutti caratteri)
- Quando si converte una stringa letterale o variabile da
NVARCHAR
(ovvero Unicode / UTF-16 / tutti i caratteri) a VARCHAR
(set di caratteri basato sulla pagina di codice specificata nella maggior parte delle regole di confronto), viene utilizzata la funzione di confronto predefinita del database
- Se la pagina di codice della collation utilizzata per la conversione non contiene lo stesso carattere, ma contiene un mapping "best fit", verrà utilizzato il mapping "best fit".
- Se la pagina di codice della collation utilizzata per la conversione non contiene lo stesso carattere o contiene una mappatura "best fit", verrà utilizzato il carattere "sostituzione" predefinito (più comunemente
?
).
Quindi, ciò che state vedendo è una NVARCHAR
per VARCHAR
la conversione a causa della mancanza del N
prefisso su quello letterale stringa. Inoltre, la pagina di codice delle regole di confronto predefinite per il database non contiene esattamente lo stesso carattere, ma è stata trovata una mappatura "best fit", motivo per cui si ottiene un 2
anziché un ?
.
Puoi vedere questo effetto eseguendo il seguente semplice test:
SELECT '₂', N'₂';
Ritorna:
2 ₂
Per essere chiari, SE la pagina di codice della raccolta predefinita per il database contenesse lo stesso identico carattere, sarebbe stata tradotta nello stesso carattere in quella pagina di codice. E, quindi, nel tuo caso, dal momento che stai memorizzando in una NVARCHAR
colonna, si sarebbe tradotto di nuovo, tornando al carattere Unicode originale. L'esempio finale di seguito mostra questo comportamento.
IMPORTANTE: tenere presente che la conversione avviene quando viene interpretato il valore letterale della stringa, ovvero prima che venga memorizzato nella colonna. Ciò significa che anche se la colonna può contenere quel carattere, sarà già stata convertita in qualcos'altro, in base alle regole di confronto predefinite del database, il tutto a causa dell'abbandono del N
prefisso su quella stringa letterale. E questo è esattamente ciò che stai (o stavi vivendo).
Ad esempio, se le regole di confronto predefinite del database sarebbero state una delle regole di confronto coreane (uno dei quattro set di caratteri a doppio byte), non si vedrebbe questo problema poiché il carattere "Sottoscrizione 2" è disponibile in quel personaggio set (Codice Pagina 949). Prova a vedere il seguente test (utilizza le regole di confronto della colonna anziché le regole di confronto predefinite del database in quanto è più facile da mostrare):
CREATE TABLE #TestChar
(
[8bit_Latin1_General-1252] VARCHAR(2) COLLATE Latin1_General_100_CI_AS_SC,
[8bit_Korean-949] VARCHAR(2) COLLATE Korean_100_CI_AS_SC,
[UTF16LE_Latin1_General-1252] NVARCHAR(2) COLLATE Latin1_General_100_CI_AS_SC
);
INSERT INTO #TestChar VALUES (N'₂', N'₂', N'₂');
SELECT * FROM #TestChar;
Ritorna:
8bit_Latin1_General-1252 8bit_Korean-949 UTF16LE_Latin1_General-1252
2 ₂ ₂
Come puoi vedere, le Latin1_General Collation, che usano Code Page 1252 (stessa code page utilizzata dalle Modern_Spanish
Collation) per i VARCHAR
dati, non hanno una corrispondenza esatta, ma hanno una mappatura "best fit" (che è ciò che stai vedendo ). MA, le regole di confronto coreane, che usano il codice pagina 949 per i VARCHAR
dati, hanno una corrispondenza esatta per il carattere "Sottoscrizione 2".
Per illustrare ulteriormente, possiamo creare un nuovo database con una raccolta predefinita di una delle regole di confronto coreane e quindi eseguire l'SQL esatto che si trova nella domanda:
CREATE DATABASE [TestKorean-949] COLLATE Korean_100_CI_AS_KS_WS_SC;
ALTER DATABASE [TestKorean-949] SET RECOVERY SIMPLE;
GO
USE [TestKorean-949];
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');
SELECT * FROM test WHERE id = 1;
UPDATE test SET description = 'CO₂' WHERE id = 1;
SELECT * FROM test WHERE id = 1;
Ritorna:
id description
1 CO2
id description
1 CO₂
AGGIORNARE
Per chiunque sia interessato a scoprire di più su cosa sta succedendo esattamente qui (cioè tutti i dettagli cruenti), si prega di consultare l'indagine in due parti che ho appena pubblicato: