この表を考えると:
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;
更新は一致するが効果がないため:
id description
----------- -----------
1 CO2
(1 affected rows)
(1 affected rows)
id description
----------- -----------
1 CO2
(1 affected rows)
₂は明らかに小さな2であるため、SQL Serverがそれを決定したかのように、最終的な値は変更されないため、変更する価値はありません。
誰かがこれにいくつかの光を当て、多分(中間値に更新する以外の)回避策を提案できますか?
下付き文字2はvarchar文字セットの一部ではありません(どの照合でも、Modern_Spanishだけではありません)。だからそれをnvarchar定数にします:
UPDATE test SET description = N'CO₂' WHERE id = 1;
@gbnはすでに基本的な理由と修正を説明していますが、表示されている動作の具体的な理由は次のとおりです。
VARCHAR
リテラル(N
プレフィックス付きの文字列)の代わりにNVARCHAR
リテラル(N
プレフィックスなし)を使用しているため、Unicode文字が変換されますVARCHAR
に入れます。VARCHAR
は8ビットエンコーディングで、ほとんどの場合、1文字あたり1バイトですが、1文字あたり2バイトにすることもできます。一方、NVARCHAR
は1文字あたり2バイトまたは4バイトの16ビットエンコーディング(UTF-16リトルエンディアン)です。VARCHAR
データは、シングルバイト文字セット(その大部分)で最大256文字、ダブルバイト文字セット(これらの一部のみ)で最大65,536文字です。一方、NVARCHAR
データは、110万個を超えるUnicode文字をマップできます(現在、25万個未満がマップされています)。VARCHAR
データで実行できるマッピングの数が限られているため、異なる言語のグループ(言語/文化に基づく)が複数の「コードページ」(つまり、文字セット)に分散されます。VARCHAR
データに使用するコードページ(ある場合)を指定します(NVARCHAR
はすべての文字です)NVARCHAR
(つまり、Unicode/UTF-16 /すべての文字)からVARCHAR
(ほとんどの照合で指定されているコードページに基づく文字セット)に変換すると、デフォルトの照合データベースの?
)。したがって、文字列リテラルにNVARCHAR
プレフィックスがないため、表示されているのはVARCHAR
からN
への変換です。また、データベースのデフォルトの照合順序のコードページにはまったく同じ文字が含まれていませんが、「最適な」マッピングが見つかりました。これが、2
ではなく?
を取得する理由です。 。
この効果は、次の簡単なテストを行うことで確認できます。
SELECT '₂', N'₂';
戻り値:
2 ₂
明確にするために、データベースのデフォルトの照合順序のコードページにまったく同じ文字が含まれている場合、そのコードページでは同じ文字に変換されます。そして、あなたの場合、NVARCHAR
列に格納しているので、元のUnicode文字に再度変換されます。以下の最後の例は、この動作を示しています。
重要:文字列リテラルが解釈されるときに変換が行われることに注意してください。つまり、before列に格納されます。これは、列がその文字を保持できる場合でも、データベースのデフォルトの照合に基づいて、既に別の文字列に変換されていることを意味します。これは、すべてその文字列リテラルのN
接頭辞を省略したためです。そして、これはまさにあなたが経験している(または経験していた)ものです。
たとえば、データベースのデフォルトの照合順序が韓国語の照合順序の1つ(4つの2バイト文字セットの1つ)である場合、その文字で「下付き文字2」の文字を使用できるため、この問題は発生しません。セット(コードページ949)。次のテストを試してみてください(表示する方が簡単なので、データベースのデフォルトの照合順序ではなく、列の照合順序を使用します)。
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;
戻り値:
8bit_Latin1_General-1252 8bit_Korean-949 UTF16LE_Latin1_General-1252
2 ₂ ₂
ご覧のとおり、VARCHAR
データに対してコードページ1252(Modern_Spanish
照合が使用するのと同じコードページ)を使用するLatin1_General照合は、完全に一致していませんが、 "最適な」マッピング(これが表示されているものです)。ただし、VARCHAR
データにコードページ949を使用する韓国語照合では、「下付き文字2」文字と完全に一致します。
さらに説明するために、韓国語の照合順序のいずれかのデフォルトの照合順序で新しいデータベースを作成し、問題のSQLを実行できます。
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;
戻り値:
id description
1 CO2
id description
1 CO₂
[〜#〜]更新[〜#〜]
ここで何が正確に行われているのか(つまり、すべての悲惨な詳細)に興味がある人は、私が投稿した2つの部分からなる調査を参照してください。