web-dev-qa-db-ja.com

UNIQUEIDENTIFIERの代わりにBINARY(16)を使用するとペナルティはありますか?

最近、Guidを格納するためにUNIQUEIDENTIFIERではなくBINARY(16)を使用するSQL Serverデータベースを継承しました。主キーを含むすべてに対してこれを行います。

心配すべきですか?

19
Jonathan Allen

心配する必要がありますか?

さて、ここで少し気になることがいくつかあります。

最初:UNIQUEIDENTIFIER(つまりGuid)が16バイトのバイナリ値であることは事実ですが、次のことも当てはまります。

  1. すべてのデータはバイナリ形式で格納できます(たとえば、INTBINARY(4)に格納でき、DATETIMEBINARY(8)に格納できます)。したがって、 #2↴
  2. 単なる利便性以外のGUIDのデータ型を別に持つ理由はおそらくあります(たとえば、NVARCHAR(128)のエイリアスとしてのsysname)。

私が見つけることができる3つの行動の違いは次のとおりです。

  • SQL ServerでのUNIQUEIDENTIFIER値の比較は、良くも悪くも、実際にBINARY(16)値を比較する方法とは異なります。 MSDNページによると、SQL ServerでUNIQUEIDENTIFIER値を比較する場合、 (GUIDとuniqueidentifier値の比較 を比較します。

    値の最後の6バイトが最も重要です

  • これらの値は頻繁にソートされませんが、これら2つのタイプにはわずかな違いがあります。 niqueidentifier のMSDNページによると:

    2つの値のビットパターンを比較することによる順序付けは実装されていません。

  • SQL Serverと.NETの間でGUID値の処理方法に違いがあることを考慮して(上記の「GUIDとuniqueidentifier値の比較」ページに記載)、このデータをSQL Serverの比較動作をエミュレートする必要がある場合、アプリコードへのSQL Serverがアプリコードで適切に処理されない可能性があります。その動作はSqlGuidに変換することでエミュレートできますが、開発者はそれを行うことを知っていますか?

2番目:次のステートメントに基づく

主キーを含むすべてに対してこれを行います。

PKとしてINTまたはBIGINTを使用するとともに、代替キーとしてではなくGUIDをPKとして使用することで、システムパフォーマンスを全般的に懸念します。また、これらのGUID PKがクラスター化インデックスである場合は、さらに心配になります。

更新

O.P.が@Robの回答について行った次のコメントは、さらに懸念を引き起こします。

mySQLから移行したと思います

GUIDは 2つの異なるバイナリ形式 で保存できます。したがって、以下に応じてが懸念の原因になる可能性があります

  1. バイナリ表現が生成されたシステム、および
  2. 文字列値がアプリのコードなどの元のシステムの外で使用されたか、インポートファイルで使用するためにクライアントに与えられた場合.

バイナリ表現が生成された場所の問題は、4つの「フィールド」のうち最初の3つのバイト順序に関係しています。上記のWikipediaの記事へのリンクをたどると、RFC 4122で4つのフィールドすべてに「ビッグエンディアン」エンコーディングを使用するように指定されているのに、Microsoft GUIDでは「ネイティブ」エンディアンを使用して指定していることがわかります。まあ、Intelアーキテクチャはリトルエンディアンなので、最初の3つのフィールドのバイト順は、RFCに準拠したシステム(およびビッグエンディアンシステムで生成されたMicrosoftスタイルのGUID)と逆になります。最初のフィールド「データ1」は4バイトです。 1つのエンディアンネスでは、(仮説的に)0x01020304として表されます。しかし、他のエンディアンでは、それは0x04030201になります。したがって、現在のデータベースのBINARY(16)フィールドが、0x01020304バイナリ表記を使用してインポートファイルから入力され、バイナリ表現がRFCに従ってシステムで生成された場合、変換現在BINARY(16)フィールドからUNIQUEIDENTIFIERへのデータは、最初に作成されたものとは異なるGUIDになります。これは実際には問題を引き起こしません[〜#〜] if [〜#〜]値がデータベースから出なかった場合、and値は、順序付けではなく、等しいかどうかのみ比較されます。

順序付けの問題は、UNIQUEIDENTIFIERに変換した後に同じ順序にならないことだけです。幸いなことに、元のシステムが実際にMySQLである場合、MySQLには文字列表現 [〜#〜] uuid [〜#〜] しかないため、最初にバイナリ表現で順序付けが行われることはありませんでした。

バイナリ表現がWindows/SQL Serverの外部で生成された場合、データベースの外部で使用される文字列値に関する懸念はさらに深刻です。バイト順序が異なる可能性があるため、文字列形式の同じGUIDは、変換が行われた場所に応じて、2つの異なるバイナリ表現になります。アプリコードまたは顧客に文字列形式のGUIDがABCとして123およびのバイナリ形式から取得され、バイナリ表現が生成された場合RFCに従ったシステムでは、同じバイナリ表現(つまり、123)は、DEFに変換されると、UNIQUEIDENTIFIERの文字列形式に変換されます。同様に、ABCの元の文字列形式は、UNIQUEIDENTIFIERに変換すると、456のバイナリ形式に変換されます。

したがって、GUIDがデータベースから出ていない場合は、順序付け以外のことについてはそれほど心配する必要はありません。または、MySQLからのインポートが文字列形式(つまり、FCCEC3D8-22A0-4C8A-BF35-EC18227C9F40)の変換によって行われた場合は、問題ない可能性があります。そうでない場合、それらのGUIDが顧客またはアプリのコードに与えられている場合は、それらを取得してSELECT CONVERT(UNIQUEIDENTIFIER, 'value found outside of the database');を介して変換することにより、それらがどのように変換されるかをテストし、期待されるレコードを見つけることができます。レコードを照合できない場合は、フィールドをBINARY(16)のままにする必要があります。

おそらく問題はありませんが、適切な条件下では問題が発生する可能性があるため、これについて言及します。

とにかく新しいGUIDはどのように挿入されますか?アプリのコードで生成されましたか?

アップデート2

別のシステムで生成されたGUIDのバイナリ表現のインポートに関連する潜在的な問題についての前の説明が少し(または多く)混乱している場合、うまくいけば、以下が少し明確になるでしょう。

DECLARE @GUID UNIQUEIDENTIFIER = NEWID();
SELECT @GUID AS [String], CONVERT(BINARY(16), @GUID) AS [Binary];
-- String = 5FED23BE-E52C-40EE-8F45-49664C9472FD
-- Binary = 0xBE23ED5F2CE5EE408F4549664C9472FD
--          BE23ED5F-2CE5-EE40-8F45-49664C9472FD

上記の出力では、「文字列」と「バイナリ」の値は同じGUIDからのものです。 「Binary」行の下の値は「Binary」行と同じ値ですが、「String」行と同じスタイルでフォーマットされます(つまり、「0x」が削除され、4つのダッシュが追加されます)。最初と3番目の値を比較すると、それらは正確ではありませんが、非常に近いです。右端の2つのセクションは同じですが、左端の3つのセクションはない。ただし、よく見ると、3つのセクションのそれぞれで同じバイトであり、順序が異なっていることがわかります。最初の3つのセクションのみを表示してバイトに番号を付けると、2つの表現の順序がどのように異なるかを確認しやすくなるので、簡単に確認できます。

文字列= 15F2ED234BE 5E562C 7408EE
バイナリ= 4BE232ED15F 62C5E5 8EE740(Windows/SQL Serverの場合)

したがって、各グループ内では、バイトの順序が逆になりますが、Windows内とSQL Server内のみです。ただし、RFCに準拠しているシステムでは、バイト順の逆転がないため、バイナリ表現はスティング表現をミラーリングします。

MySQLからSQL Serverにデータはどのように取り込まれましたか?ここにいくつかの選択肢があります:

SELECT CONVERT(BINARY(16), '5FED23BE-E52C-40EE-8F45-49664C9472FD'),
       CONVERT(BINARY(16), 0x5FED23BEE52C40EE8F4549664C9472FD),
    CONVERT(BINARY(16), CONVERT(UNIQUEIDENTIFIER, '5FED23BE-E52C-40EE-8F45-49664C9472FD'));

戻り値:

0x35464544323342452D453532432D3430  
0x5FED23BEE52C40EE8F4549664C9472FD  
0xBE23ED5F2CE5EE408F4549664C9472FD

バイナリからバイナリへの変換(上記の#2の変換)であると仮定すると、結果のGUIDは、実際のUNIQUEIDENTIFIERに変換すると、次のようになります。

SELECT CONVERT(UNIQUEIDENTIFIER, 0x5FED23BEE52C40EE8F4549664C9472FD);

戻り値:

BE23ED5F-2CE5-EE40-8F45-49664C9472FD

それは間違っています。そして、それは私たちに3つの質問を残す:

  1. データはどのようにしてSQL Serverにインポートされましたか?
  2. アプリのコードはどの言語で書かれていますか?
  3. アプリのコードはどのプラットフォームで実行されていますか?
21
Solomon Rutzky

あなたはいつも心配することができます。 ;)

システムは、uniqueidentifierをサポートしていない他のシステムから移行された可能性があります。あなたが知らない他の妥協はありますか?

設計者は、uniqueidentifierタイプについて知らなかった可能性があります。彼らは他に何を知らなかったのですか?

技術的には-それは大きな問題ではありません。

5
Rob Farley