web-dev-qa-db-ja.com

SQLServerでvarbinaryデータをvarcharに変換すると予期しない結果が生じる

SQL Server 2008のデータ変換の問題を修正する必要があります。データの保存に関する要件の変更がありました。あまり注意せずに、CONVERT(NVARCHAR(max), @bytearraydata, 1)を使用して既存のvarbinaryデータをvarcharに変換しました。

C#での同じ変換は、_Encoding.Default.GetString_および_Encoding.Default.GetBytes_メソッドを使用して行われます。 Encoding.Default.GetBytes(string)は、以前のようにバイト配列を取得します。しかし、CONVERT()を使用して変換した文字列のバイト配列を取得しようとすると、間違った結果が返されます。

私の仕事は、データベースとして文字列として格納されているバイト配列をフェッチしてバイト配列に変換し、最後にコンテンツをPDFとしてレンダリングすることです。 (保存中およびフェッチ中に)エンコーディングメカニズムを通過するデータは、私にとってはうまく機能します。しかし、CONVERTを使用して変換されたデータをフェッチしようとすると、PDFの生成に失敗します。

この問題を解決するにはどうすればよいですか?

概要:

バイト配列の列が文字列に変更されました。

この関数を使用して行われる既存のデータ変換:

_Convert(NVARCHAR(MAX), @bytearraydata, 1)
_

アプリケーションでは、バイト配列変換はEncoding.Default.GetString(bytearraydata)を使用して行われます

EncodingCONVERTは互換性がありませんか?

3
user3169103

Encoding.Defaultを使用すると、結果はローカル設定に依存します。

オペレーティングシステムの現在のANSIコードページのエンコーディング。

仕様は、デフォルトのエンコーディングを使用することの危険性についてかなり直接的であり、非常に具体的には、それを使用するための推奨notを呼び出します。

異なるコンピューターは異なるエンコーディングをデフォルトとして使用でき、デフォルトのエンコーディングは単一のコンピューターでも変更できます。そのため、あるコンピューターから別のコンピューターにストリーミングされたデータ、または同じコンピューターで異なる時間に取得されたデータも、正しく変換されない場合があります。さらに、Defaultプロパティによって返されるエンコーディングは、最適なフォールバックを使用して、サポートされていない文字をコードページでサポートされている文字にマップします。これら2つの理由により、通常はデフォルトのエンコーディングを使用することはお勧めしません

ここで、何らかの理由で、現在のランダムなローカルエンコーディングがサーバーのエンコーディングと一致することを期待します。 CONVERT関数があなたが信じていることをするとしても、結果はランダムで予測不可能です:

  • サーバーのローカルコードページは、クライアントのコードページと異なる場合があります。
  • コードページを変更すると、永続化されたデータが1つのエンコーディングで書き込まれ、別のエンコーディングで読み取ろうとしたため、データが読み取れなくなります。

さらに、CONVERTは期待どおりのことを行いません。 CONVERTは、UCS-2エンコーディングを使用してVARBINARYNVARCHARにキャストします。これは、SQL ServerがNVARCHARデータに使用するエンコーディングであるためです。

最初にこの記事を読んで、緊急の問題に取り組むことをお勧めします すべてのソフトウェア開発者の絶対的な最小値絶対に、確実にUnicodeと文字セットについて知っておく必要があります(言い訳なし!)SQL Serverの国際的な考慮事項 でフォローアップします。

1
Remus Rusanu

質問にはいくつかの混乱があり、予期しない結果につながります。

  1. VARCHARNVARCHARという用語は同じ意味で使用されています(またはそう思われます)が、まったく異なります。 NVARCHARは16ビットエンコーディング–正確にはUTF-16 LE(リトルエンディアン)–であり、これは変更されません。 VARCHARは8ビットエンコーディングであり、使用される特定の8ビットエンコーディングは、列の照合に関連付けられたコードページによって決定されます(文字列リテラルのVARCHARデータは無視します)この質問はテーブルに保存されたデータに関するものなので、今のところ変数)。特定の照合順序に関連付けられているコードページを知りたい場合は、COLLATIONPROPERTY組み込み関数を使用できます。

    _SELECT COLLATIONPROPERTY(N'Latin1_General_100_CI_AS_SC', 'CodePage') AS [CodePage];
    -- 1252
    _
  2. VARBINARYVARCHARまたはNVARCHARの間で変換するときは、その文字列データ型と一致するように注意する必要があります。 VARCHARからVARBINARYに変換してから同じVARBINARYを使用してNVARCHARに変換することはできません。

  3. .NETのEncodingクラスは、7ビット、8ビット、16ビット、32ビット、または変数(UTF-8など)のいずれであっても、テキストの特定のエンコーディングを表します。 「期待どおりの」結果を得るには、_byte[]_表現に関して、変換先または変換元と一致するエンコードを作成する必要があります。 .NETの文字列は常にUTF-16 LE(NVARCHARと同じ)であり、これが.NETのUnicodeエンコーディングと呼ばれています。エンコーディングの_byte[]_表現は、作成された任意のエンコーディングになりますが、文字列表現は常にUTF-16 LEになります。したがって、どのエンコーディングを作成するかは、処理するデータのタイプによって異なります。

    • NVARCHAR: _Encoding.Unicode_を使用
    • VARCHAR:COLLATIONPROPERTY(N'collation_name', 'CodePage')を使用して照合順序のコードページを決定し、int値をEncoding.GetEncoding(CodePageIntValue)で使用します。
  4. CONVERT組み込み関数を使用するときは、使用している「スタイル」番号に注意してください。例えば:

    _SELECT CONVERT(VARBINARY(50), N'bob');
    -- 0x62006F006200
    _

    次に、返されたVARBINARY値を取得し、「スタイル」の値0(デフォルト)と1(NVARCHAR関数で使用している値)を使用してCONVERTに戻します。質問):

    _SELECT CONVERT(NVARCHAR(MAX), 0x62006F006200, 0) AS [Style_0],
           CONVERT(NVARCHAR(MAX), 0x62006F006200, 1) AS [Style_1];
    _

    戻り値:

    _Style_0        Style_1
    bob            0x62006F006200
    _

したがって、質問の次のステートメントが真である場合:

アプリケーションでは、バイト配列変換はEncoding.Default.GetString(bytearraydata)を使用して行われます

次に、VARCHARの代わりにNVARCHARを使用し、_0_の代わりに_1_(または何もない)の「スタイル」値を使用することと同じになります。

_CONVERT(VARCHAR(MAX), 0x62006F006200)
_
1
Solomon Rutzky

その問題を再現することはできません。余分な手順はありましたか?私はテキストをバイナリに変換し、再び、またはその逆に、損失なしに変換できます。

DECLARE @OrigText      VARCHAR  (100) = 'There once was a bear'
DECLARE @Binary        VARBINARY(100) = CONVERT(VARBINARY(100), @OrigText)
DECLARE @RoundTripText VARCHAR  (100) = CONVERT(VARCHAR  (100), @Binary)
DECLARE @RoundTripBin  VARBINARY(100) = CONVERT(VARBINARY(100), @RoundTripText)

SELECT @OrigText, @Binary, @RoundTripText, @RoundTripBin

結果:

  • かつてクマがいた
  • 0x5468657265206F6E63652077617320612062656172
  • かつてクマがいた
  • 0x5468657265206F6E63652077617320612062656172

これはNVARCHARでも機能し、CASTではなくCONVERTを使用します。 CONVERTのスタイルを指定していないことに注意してください。 1つ指定した場合、私の理解は、テキストは16進数の文字列である必要があるということです。それはあなたが保存しているものですか、それとも従来のテキストですか?

0