データベースのデフォルトの照合順序を変更したときのLatin1_General_BINのパフォーマンスへの影響


16

データベース照合をに設定して、Latin1_General_BIN文字列比較で大文字と小文字を区別します。これはパフォーマンスに影響しますか?データベースのDMLまたはDDL操作に影響はありますか?データベースは既にテーブルとともに存在しています。

回答:


24

SQL Serverの照合順序は、文字データの照合と並べ替えのルールを決定します。通常、比較セマンティクスとデータのコンシューマーが必要とするソート順に基づいて、最初に照合を選択します。

人間は通常、バイナリ照合が期待するソートおよび比較動作を生成することを発見しません。そのため、これらは最高のパフォーマンス(特に純粋なコードポイントBIN2バージョン)を提供しますが、ほとんどの実装では使用されません。

次に、未加工のパフォーマンス用語で(ただし、非Unicode文字列のみ)下位互換性のあるSQL照合順序です。Unicodeデータを操作する場合、これらの照合は同じパフォーマンス特性を持つWindows照合を代わりに使用します。ここには微妙なtrapがあるので、最近はSQL照合順序を選択する正当な理由が必要です(米国のシステムで作業している場合を除き、それは依然としてデフォルトです)。

一般に、Unicodeの比較と並べ替えの規則が複雑であるため、Windows照合は最も低速です。それにもかかわらず、これらはSQL ServerのWindowsとの完全な互換性を提供し、Unicode標準の変更に対応するために定期的に維持されます。Unicodeデータを含む最新の使用では、通常、Windows照合が推奨されます。

TL; DR

あなたが望むすべてがある場合は大文字と小文字を区別して比較し、セマンティクスを並べ替え、あなたが選ぶべき_CS_どのベース照合の(ための大文字と小文字は区別される)変動はユーザーの言語と文化のために予想される動作を提供します。たとえば、これらはどちらも大文字と小文字を区別する照合です。

-- Latin1-General, case-sensitive, accent-sensitive
Latin1_General_CS_AS 

-- Latin1-General, case-sensitive, accent-sensitive for Unicode Data, 
-- SQL Server Sort Order 51 on Code Page 1252 for non-Unicode Data
SQL_Latin1_General_CP1_CS_AS

sys.fn_helpcollat​​ionsを使用してこれらの定義を確認できます

照合順序を除いてまったく同じ4つのテーブル。1つのバイナリ、1つの大文字と小文字を区別、1つの大文字と小文字を区別しない、1つのSQLの大文字と小文字を区別します。

CREATE TABLE #Example_BIN
(
    string nvarchar(50) 
        COLLATE Latin1_General_BIN
        NOT NULL
);

CREATE TABLE #Example_CS
(
    string nvarchar(50) 
        COLLATE Latin1_General_CS_AI
        NOT NULL
);

CREATE TABLE #Example_CI
(
    string nvarchar(50) 
        COLLATE Latin1_General_CI_AI
        NOT NULL
);

CREATE TABLE #Example_SQL
(
    string varchar(50) -- Note varchar
        COLLATE SQL_Latin1_General_CP1_CS_AS
        NOT NULL
);

各テーブルに同じサンプルデータ

INSERT #Example_BIN
    (string)
VALUES
    (N'A'),
    (N'a'),
    (N'B'),
    (N'b'),
    (N'C'),
    (N'c');

INSERT #Example_CS
SELECT EB.string 
FROM #Example_BIN AS EB;

INSERT #Example_CI
SELECT EB.string 
FROM #Example_BIN AS EB;

INSERT #Example_SQL
SELECT EB.string 
FROM #Example_BIN AS EB;

次に、「a」より大きい文字列検索します

SELECT EB.string AS BIN
FROM #Example_BIN AS EB
WHERE EB.string > N'a'
ORDER BY EB.string;

SELECT EC.string AS CS
FROM #Example_CS AS EC
WHERE EC.string > N'a'
ORDER BY EC.string;

SELECT EC2.string AS CI
FROM #Example_CI AS EC2
WHERE EC2.string > N'a'
ORDER BY EC2.string;

SELECT ES.string AS SQL
FROM #Example_SQL AS ES
WHERE ES.string > 'a' -- not Unicode
ORDER BY ES.string;

結果:

╔═════╗
 BIN 
╠═════╣
 b   
 c   
╚═════╝

╔════╗
 CS 
╠════╣
 A  
 b  
 B  
 c  
 C  
╚════╝

╔════╗
 CI 
╠════╣
 B  
 b  
 C  
 c  
╚════╝

╔═════╗
 SQL 
╠═════╣
 B   
 b   
 C   
 c   
╚═════╝

最後に...

ただし、SQL照合でUnicodeリテラルを使用すると、暗黙的な変換ルールによりWindows照合の比較が行われます。

SELECT ES.string AS SQL
FROM #Example_SQL AS ES
WHERE ES.string > N'a'
ORDER BY ES.string;

...そしてSQL照合結果が変わります

╔═════╗
 SQL 
╠═════╣
 A   
 B   
 b   
 C   
 c   
╚═════╝

10

これは既にテーブルが定義されている既存のデータベースであるため、データベースの照合順序を変更するアクションには、DML操作への潜在的なパフォーマンスの影響(実際には既に存在している)を超えて、いくつかの非常に深刻な影響があります。パフォーマンスと機能に非常に現実的な影響があり、この変更は意図した目標を達成しなかっただけでなく(少なくとも一貫していない)、同様に動作を変更した(または新しいテーブルが作成されたときに動作を変更します)データの順序付けと同等化の方法。

ポールはすでに、彼の回答でさまざまな種類の照合間のパフォーマンスと動作の違いについての良い説明と例を提供しているので、ここでは繰り返しません。ただし、いくつかのポイントには追加の詳細が必要です。また、新しいDBの照合を設定するのではなく、既存のDBの照合を変更する現在のシナリオに関連して追加するいくつかのポイントがあります。

  1. バイナリ照合は、大文字と小文字を区別するだけではありません。すべて大文字と小文字が区別されます。したがって、バイナリ照合(_BINまたはで終わる_BIN2)を使用することにより、比較もアクセントを区別し、かなを区別し、幅を区別し、潜在的にグルテンを区別します(少なくとも最近の傾向のようです;-))。これは、この変更を行うことの望ましい影響でしたか?エンドユーザーはこの動作の変更を期待していますか?

  2. 照合順序は、比較だけでなく、並べ替えにも影響します。バイナリ照合順序は、各byteASCIIor UNICODEバイト値(VARCHARまたはor NVARCHARに依存)に基づいてソートされます。したがって、バイナリ照合順序を選択すると、その文化のアルファベットに従って各文字(ハンガリー語などの一部の言語の文字でさえ2文字で構成される)を順序付ける言語/文化固有の重み付けルールを放棄します。したがって、 "ch"が"k"の後に自然に来る場合、それはバイナリ照合を使用して発生しません。繰り返しますが、これはこの変更を行うことの望ましい影響でしたか?エンドユーザーはこの動作の変更を期待していますか?

  3. アプリケーションに特定の下位互換性要件がない限り、当然のことながら、最初にバイナリ照合が必要であることを前提として、照合のBIN2代わりにBIN照合を使用する必要があります。BIN2照合順序は、SQL Server 2005で導入された、とのためのMSDNのページに応じたBINとBIN2照合順序を使用するためのガイドライン

    "_BIN"で終わるSQL Serverの以前のバイナリ照合は、Unicodeデータのコードポイント間比較が不完全でした。古いSQL Serverのバイナリ照合では、最初の文字がWCHARとして比較され、その後にバイト単位の比較が行われました。

    ...

    [_BIN2]バイナリ照合順序に移行して、真のコードポイント比較を利用できます。新しいアプリケーションの開発には、新しいバイナリ照合順序を使用する必要があります。

    また、_BIN2照合OrdinalStringComparison Enumerationのオプションの動作と便利に一致し、そのオプションを使用して.NETコードで行われた比較と並べ替えは、SQL Server内で実行される同じ操作と同じ結果を生成することに注意してください_BIN2もちろん、照合)。

  4. ちょうどについて述べたものと同様の理由で_BIN2照合、あなたは後方互換性の振る舞いを維持するために特定の要件がない限り、あなたが始まるものはWindows照合順序ではなくSQL Server固有の照合順序(つまりを使用した方に傾くはずですSQL_、今考えられていますちょっと「サッキー」;-))。

  5. Unicodeデータ(つまりN、データ型がNCharor として指定されているアプリコードからプレフィックスまたはSQL Serverに入力される文字列NVarChar)を使用する場合、ある照合順序と別の照合順序を使用して、NCHARまたはNVARCHAR文字列フィールドを挿入または更新する方法がわかりません。

    非Unicodeデータを使用する場合、または非Unicodeフィールドに挿入または更新する場合、挿入/更新される文字を翻訳する必要がある場合、またはマッピング可能でない場合(特定の照合(データベースまたはフィールド)小さな役割を果たします照合によって定義されているコードページで指定されているとおりです。もちろん、この潜在的な問題は、非Unicodeデータまたはデータ型を使用している場合に必ず存在し、DB照合順序を変更するこのシナリオに固有のものではありません。この変更は、文字列リテラルに影響します(DB照合がフィールドの照合と異なる場合は、すでに問題になっている可能性があります)。ただし、DB照合に変更が加えられていない場合でも、他のDBまたはSQL Serverの外部(任意のクライアントコード)から受信するデータには、任意の文字を含めることができ、特定のエンコードを使用できます。

  6. 非常に重要です!!! データベースの既定の照合順序を変更する場合、既存のテーブルの既存の文字列フィールドに指定された照合順序変更されませんが、新しいフィールドにはデータベースの既定の照合順序が適用されます(COLLATE句によってオーバーライドされない限り)。これは、次の3つの方法でクエリに影響を与えます。

    1)それらの既存のフィールドのいずれかを新しいフィールドのいずれかにクエリする場合、照合不一致エラーが発生します。

    USE [master];
    GO
    
    IF (DB_ID(N'ChangeCollationTest') IS NOT NULL)
    BEGIN
        PRINT 'Dropping [ChangeCollationTest] DB...';
        ALTER DATABASE [ChangeCollationTest]
            SET SINGLE_USER
            WITH ROLLBACK IMMEDIATE;
    
        DROP DATABASE [ChangeCollationTest];
    END;
    GO
    
    PRINT 'Creating [ChangeCollationTest] DB...';
    CREATE DATABASE [ChangeCollationTest]
        COLLATE SQL_Latin1_General_CP1_CI_AS;
    GO
    
    USE [ChangeCollationTest];
    GO
    
    CREATE TABLE [CollateTest-SQL_Latin1_General_CP1_CI_AS]
                 (Col1 NVARCHAR(50) COLLATE DATABASE_DEFAULT, Col2 NVARCHAR(50));
    SELECT *
    FROM   sys.columns sc
    WHERE  sc.[object_id] = OBJECT_ID(N'[CollateTest-SQL_Latin1_General_CP1_CI_AS]');
    -- "collation_name" for both fields shows: SQL_Latin1_General_CP1_CI_AS
    GO
    
    USE [master];
    GO
    ALTER DATABASE [ChangeCollationTest]
        COLLATE Latin1_General_BIN2;
    GO
    USE [ChangeCollationTest];
    GO
    
    CREATE TABLE [CollateTest-Latin1_General_BIN2]
                 (Col1 NVARCHAR(50) COLLATE DATABASE_DEFAULT, Col2 NVARCHAR(50));
    SELECT *
    FROM   sys.columns sc
    WHERE  sc.[object_id] = OBJECT_ID(N'[CollateTest-Latin1_General_BIN2]');
    -- "collation_name" for both fields shows: Latin1_General_BIN2
    GO
    
    
    SELECT *
    FROM   dbo.[CollateTest-SQL_Latin1_General_CP1_CI_AS] ctSQL
    INNER JOIN  dbo.[CollateTest-Latin1_General_BIN2] ctWIN
            ON  ctWIN.Col1 = ctSQL.Col1;

    戻り値:

    Msg 468, Level 16, State 9, Line 4
    Cannot resolve the collation conflict between "SQL_Latin1_General_CP1_CI_AS" and
    "Latin1_General_BIN2" in the equal to operation.

    2)文字列リテラルまたは変数と比較する既存のテーブルの既存のフィールド(以前の既定の照合に設定)の述語/フィルターはエラーになりませんが、SQL Serverの照合を一致させる必要があるため、パフォーマンスに影響する可能性があります文字列リテラルまたは変数をフィールドの照合に自動的に変換します。[実際の実行計画を含める](Control-M)を有効にしてから、次を実行します(上記のクエリを既に実行していると仮定)。

    SELECT *
    FROM   dbo.[CollateTest-SQL_Latin1_General_CP1_CI_AS] ctSQL
    WHERE  ctSQL.Col1 = N'a';
    -- Unspecified collations on string literals and variables assume the database default
    -- collation. This mismatch doesn't cause an error because SQL Server adds a
    -- "[Col1]=CONVERT_IMPLICIT(nvarchar(4000),[@1],0)" but it can hurt performance.
    
    SELECT *
    FROM   dbo.[CollateTest-Latin1_General_BIN2] ctWIN
    WHERE  ctWIN.Col1 = N'a';
    -- No CONVERT_IMPLICIT; plan shows "[Col1]=[@1]".

    3)と、それはデータベースのデフォルトの照合の暗黙の照合と文字列リテラル(どのように通知を、暗黙の変換といえば:Latin1_General_BIN2)、変換されないフィールドがテーブルに。このフィルターが大文字と小文字を区別しない(古い照合)か​​、大文字と小文字を区別する(新しい照合)か​​どうかについての推測はありますか?以下を実行して確認します。

    INSERT INTO dbo.[CollateTest-SQL_Latin1_General_CP1_CI_AS] (Col1)
    VALUES (N'a'), (N'A');
    
    SELECT ctSQL.Col1
    FROM   dbo.[CollateTest-SQL_Latin1_General_CP1_CI_AS] ctSQL
    WHERE  ctSQL.Col1 = N'a';

    戻り値:

    Col1
    ----
    a
    A

    ど!が原因で、このクエリのパフォーマンスがわずかに(またはより重要な場合があります)ヒットするCONVERT_IMPLICIT()だけでなく、大文字と小文字を区別して希望どおりに動作しません。

    エルゴ、既にテーブルがあるDBで照合順序が変更された場合、はい、パフォーマンスと機能の両方が影響を受けます。

    照合が新しいDBに設定されている場合、Paulは既にバイナリ照合が高速であるにもかかわらず、おそらく期待または希望する方法でソートされないことを説明することでそれを説明しました。


また、条件ごとに照合をいつでも指定できることに注意してください。COLLATEの句は、に追加することができWHERE、条件ORDER BY、および文字列を受け取り、ほとんど任意の場所。

例1(WHERE条件):

SELECT tmp.col AS [SQL-CaseSensitive]
FROM (VALUES ('a'), ('A'), ('b'), ('B')) tmp(col)
WHERE tmp.col > 'a' COLLATE SQL_Latin1_General_CP1_CS_AS;

SELECT tmp.col AS [Windows-CaseSensitive]
FROM (VALUES ('a'), ('A'), ('b'), ('B')) tmp(col)
WHERE tmp.col > 'a' COLLATE Latin1_General_CS_AI;

戻り値:

SQL-CaseSensitive
-----------------
b
B

Windows-CaseSensitive
-----------------
A
b
B

例2(ORDER BY):

SELECT tmp.col AS [Windows-CaseSensitive]
FROM (VALUES ('a'), ('A'), ('b'), ('B')) tmp(col)
ORDER BY tmp.col COLLATE Latin1_General_CS_AI;

SELECT tmp.col AS [Windows-Binary]
FROM (VALUES ('a'), ('A'), ('b'), ('B')) tmp(col)
ORDER BY tmp.col COLLATE Latin1_General_BIN2;

戻り値:

Windows-CaseSensitive
-----------------
a
A
b
B

Windows-Binary
-----------------
A
B
a
b

例3(IFステートメント):

IF ('A' = 'a') SELECT 1 AS [DatabaseDefault-CaseInsensitive?];
-- if the DB is not case-sensitive or binary, returns 1

IF ('A' = 'a' COLLATE Latin1_General_BIN2) SELECT 2 AS [Windows-Binary];

戻り値:

DatabaseDefault-CaseInsensitive?
--------------------------------
1

{nothing}

例4(関数の入力パラメーターに関連付ける):

SELECT  UNICODE(N'🂡') AS [UCS-2],
        UNICODE(N'🂡' COLLATE Latin1_General_100_CI_AS_SC) AS [UTF-16];
-- This character is a Unicode supplemental character and is not part of the
-- default UCS-2 encoding. In order for built-in functions to handle these
-- characters correctly, either the DB default collation needs to end in
-- "_SC" (available as of SQL Server 2012), or use as shown here.
-- See the character in more detail here: http://unicode-table.com/en/1F0A1/

戻り値:

UCS-2    UTF-16
------   -------
55356    127137

55,356のUCS-2値は、「サロゲートペア」の2つの値の最初の値であるという点で部分的に正しいです。ただし、_SC照合順序を明示的に指定しない限り、UNICODE()関数は各文字を2バイト値としてしか認識できず、2バイト文字のサロゲートペアを適切に処理する方法を知りません。


更新

上記のすべての例でさえ、通常は見落とされ、バイナリ比較/照合によって否定される大文字と小文字を区別する比較の1つの側面は、Unicodeの一部である正規化(合成と分解)です。

例5(バイナリ比較で大文字と小文字が区別されない場合):

真の大文字と小文字を区別する比較では、別の文字と組み合わせて、別のUnicodeコードポイントとして既に存在するさらに別の文字を形成する文字を組み合わせることができます。大文字と小文字を区別する比較では、作成可能なコードポイントではなく、表示可能な文字が考慮されます。

SELECT 'Equal' AS [Binary],
       NCHAR(0x00FC) AS [ü],
       N'u' + NCHAR(0x0308) AS [u + combining diaeresis]
WHERE  NCHAR(0x00FC) COLLATE Latin1_General_100_BIN2
    =  N'u' + NCHAR(0x0308) COLLATE Latin1_General_100_BIN2
-- No result as they are a different number of code points,
-- as well as being different code points.

SELECT 'Equal' AS [Case-Sensitive],
       NCHAR(0x00FC) AS [ü],
       N'u' + NCHAR(0x0308) AS [u + combining diaeresis]
WHERE  NCHAR(0x00FC) COLLATE Latin1_General_100_CS_AS -- ü
    =  N'u' + NCHAR(0x0308) COLLATE Latin1_General_100_CS_AS -- u + combining diaeresis
-- Result set returned, even being a different number of code points AND Accent Sensitive,
-- due to normalization

戻り値:

Binary            ü     u + combining diaeresis
-------          ---   -------------------------
{nothing}

Case-Sensitive    ü     u + combining diaeresis
---------------  ---   -------------------------
Equal             ü     ü

大文字と小文字を区別する真の比較では、ワイド文字を非ワイド文字と同等にすることもできます。

IF (N'sofia' = N'sofia' COLLATE Latin1_General_100_BIN2)
  SELECT 'Values are the same' AS [Binary]
ELSE
  SELECT 'Values are different' AS [Binary];


IF (N'sofia' = N'sofia' COLLATE Latin1_General_100_CS_AS)
  SELECT 'Values are the same' AS [Case-Sensitive]
ELSE
  SELECT 'Values are different' AS [Case-Sensitive];

戻り値:

Binary
---------------
Values are different


Case-Sensitive
---------------
Values are the same

エルゴ:

バイナリ(_BINおよび_BIN2)照合で大文字と小文字が区別されません

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.