@gbn zaten temel nedeni ve düzeltmeyi açıkladı, ancak gördüğünüz davranışın özel nedeni şudur:
- Bir
VARCHAR
değişmez ( N
önek NVARCHAR
içeren dize ) yerine değişmez ( N
önek yok) kullanıyorsunuz, bu nedenle Unicode karakteri dönüştürülecektir VARCHAR
.
VARCHAR
çoğu durumda karakter başına bir bayt olan 8 karakterlik bir kodlamadır, ancak karakter başına iki bayt da olabilir. Öte yandan, NVARCHAR
karakter başına iki bayt veya dört bayt olan 16 bit kodlamadır (UTF-16 Little Endian).
- Karakterleri eşlemek için kullanılabilecek bayt sayısındaki farktan dolayı, 8 bit kodlamalar, doğası gereği, eşleştirilebilecek karakter sayısında çok daha sınırlıdır.
VARCHAR
veriler Tek Baytlı Karakter Kümeleri için (çoğu) 256 karaktere kadar ve Çift Baytlı Karakter Kümeleri için 65,536 karaktere kadar (bunlardan yalnızca birkaçı). Öte yandan, NVARCHAR
veriler 1.1 milyondan fazla Unicode karakterin birazını eşleştirebilir (şu anda eşlenmiş 250k'nin biraz altında olsa da).
- 8 bit /
VARCHAR
veri ile yapılabilecek sınırlı sayıda eşleme nedeniyle , farklı karakter grupları (Dil / Kültür temelli) birden çok "Kod Sayfası" (yani karakter kümeleri) boyunca yayılır
- Her Harmanlama,
VARCHAR
veri için hangi Kod Sayfasının kullanılacağını belirtir ( NVARCHAR
tümü karakterdir)
- Bir dize hazır bilgisi veya değişkeni
NVARCHAR
(yani Unicode / UTF-16 / tüm karakterler) 'den VARCHAR
(çoğu Harmanlamada belirtilen Kod Sayfasına dayalı karakter kümesi) dönüştürülürken, varsayılan Veritabanı Harmanlaması kullanılır
- Dönüşüm için kullanılan Harmanlamanın Kod Sayfası aynı karakteri içermiyor, ancak "en uygun" eşlemeyi içeriyorsa, "en uygun" eşleme kullanılır.
- Dönüşüm için kullanılan Harmanlamanın Kod Sayfası aynı karakteri içermiyorsa veya "en uygun" eşleme içermiyorsa, varsayılan "değiştirme" karakteri (en yaygın olarak
?
) kullanılır.
Ee, ne görüyorsanız bir olduğu NVARCHAR
için VARCHAR
, eksik dönüşüm N
dize üzerinde öneki. Ve Veritabanı için varsayılan Harmanlama Kod Sayfası tam olarak aynı karakteri içermiyor, ancak bir "en uygun" eşleme bulundu, bu yüzden bir 2
yerine alıyorsunuz ?
.
Aşağıdaki basit testi yaparak bu etkiyi görebilirsiniz:
SELECT '₂', N'₂';
İadeler:
2 ₂
Açıkça belirtmek gerekirse, Veritabanı için varsayılan Harmanlama'nın Kod Sayfası tam olarak aynı karakteri içeriyorsa, o Kod Sayfasında aynı karaktere çevrilmiş olurdu. Ve sonra, sizin durumunuzda, bir NVARCHAR
sütuna depoladığınızdan , tekrar orijinal Unicode karakterine geri çevirecekti. Aşağıdaki son örnek bu davranışı göstermektedir.
ÖNEMLİ: Dönüşümün dize hazır bilgisi yorumlanırken (sütuna depolanmadan önceki) gerçekleştiğini lütfen unutmayın . Bu, sütun bu karakteri tutabilse bile, veritabanının varsayılan Harmanlaması'na göre zaten başka bir şeye dönüştürülmüş olacağı anlamına gelir N
. Ve bu tam olarak deneyimlediğiniz (veya yaşadığınız) şeydir.
Örneğin, Veritabanınızın varsayılan Harmanlaması Kore Harmanlamalarından biri olsaydı (dört Çift Baytlık Karakter Kümelerinden biri), bu karakterde "Abonelik 2" karakteri bulunduğundan bu sorunu görmezdiniz ayarlayın (Kod Sayfa 949). Aşağıdaki testi deneyin (gösterilmesi daha kolay olduğundan, Veritabanı'nın varsayılan Harmanlaması yerine sütunun Harmanlamasını kullanır):
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;
İadeler:
8bit_Latin1_General-1252 8bit_Korean-949 UTF16LE_Latin1_General-1252
2 ₂ ₂
Gördüğünüz gibi, veriler Modern_Spanish
için Kod'u (Harmanlamaların kullandığı Kod Sayfası ile birlikte) kullanan Latin1_Genel Harmanlamalar VARCHAR
tam eşleşmiyor, ancak "en uygun" eşleştirmeye sahipler (gördüğünüz şey budur) ). ANCAK, VARCHAR
veriler için Kod kullanan Kore Harfleri "Alt simge 2" karakteriyle tam olarak eşleşir.
Daha fazla açıklamak için, Korece Harmanlamalardan birinin varsayılan Harmanlaması ile yeni bir Veritabanı oluşturabilir ve ardından sorudaki tam SQL'i çalıştırabiliriz:
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;
İadeler:
id description
1 CO2
id description
1 CO₂
GÜNCELLEME
Burada tam olarak neler olduğu hakkında daha fazla bilgi edinmek isteyen herkes için (yani tüm kanlı ayrıntılar), lütfen az önce gönderdiğim iki parçalı soruşturmaya bakın: