Tablo satırındaki “CO2” yi “CO₂” olarak güncelleyemiyorum


19

Bu tablo verildiğinde:

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');

Tipografik bir sorunu çözemediğimi fark ettim:

SELECT * FROM test WHERE id = 1;
UPDATE test SET description = 'CO₂' WHERE id = 1;
SELECT * FROM test WHERE id = 1;

çünkü güncelleme eşleşiyor ancak etkisi yok:

id          description
----------- -----------
1           CO2

(1 affected rows)

(1 affected rows)

id          description
----------- -----------
1           CO2

(1 affected rows)

Çünkü SQL Server, belirlerse sanki Açıkçası sadece küçük olduğu 2 onu değiştirmektir yüzden değmez nihai değeri değişmeyecektir.

Birisi buna biraz ışık tutabilir ve belki bir geçici çözüm önerebilir (bir ara değere güncelleme yapmak dışında)?


1
Álvaro: Bu davranış hakkında daha fazla bilgi edinmek istiyorsanız, bunun neden olduğunu daha iyi anlamak için lütfen cevabımın en altına eklediğim iki bağlantıya bakın.
Solomon Rutzky

Yanıtlar:


29

Alt simge 2, varchar karakter kümesinin bir parçası değildir (sadece Modern_Spanish değil, herhangi bir harmanlamada). Bu yüzden bir nvarchar sabiti yapın:

UPDATE test SET description = N'CO₂' WHERE id = 1;

1
Sadece değeri düzeltmekle kalmayıp, ilk etapta oraya nasıl geldiğini de anladım. Teşekkür ederim!
Álvaro González

2
@ ÁlvaroGonzález ve gbn: Açıkça söylemek gerekirse, "Altyazı 2", söz konusu Veritabanının varsayılan Harmanlaması tarafından belirtilen Kod Sayfasında kullanılamaz; bu, dize değişmez değerleri ve değişkenler için kullanılan Harmanlamadır (her ikisi de aynı Kod Sayfasını kullanıyor olabilir). Ancak, "Abonelik 2", Kore Harfleri aracılığıyla Kod'da kullanılabilir. Burada işe yaramayacak, sadece FYI. Ben ayrıntıları ve benim de bir örnek var cevap .
Solomon Rutzky

21

@gbn zaten temel nedeni ve düzeltmeyi açıkladı, ancak gördüğünüz davranışın özel nedeni şudur:

  1. Bir VARCHARdeğişmez ( Nönek NVARCHARiçeren dize ) yerine değişmez ( Nönek yok) kullanıyorsunuz, bu nedenle Unicode karakteri dönüştürülecektir VARCHAR.
  2. 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, NVARCHARkarakter başına iki bayt veya dört bayt olan 16 bit kodlamadır (UTF-16 Little Endian).
  3. 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. VARCHARveriler 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, NVARCHARveriler 1.1 milyondan fazla Unicode karakterin birazını eşleştirebilir (şu anda eşlenmiş 250k'nin biraz altında olsa da).
  4. 8 bit / VARCHARveri 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
  5. Her Harmanlama, VARCHARveri için hangi Kod Sayfasının kullanılacağını belirtir ( NVARCHARtümü karakterdir)
  6. 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
  7. 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.
  8. 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 NVARCHARiçin VARCHAR, eksik dönüşüm Ndize ü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 2yerine 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 NVARCHARsü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_Spanishiçin Kod'u (Harmanlamaların kullandığı Kod Sayfası ile birlikte) kullanan Latin1_Genel Harmanlamalar VARCHARtam eşleşmiyor, ancak "en uygun" eşleştirmeye sahipler (gördüğünüz şey budur) ). ANCAK, VARCHARveriler 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:

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.