web-dev-qa-db-ja.com

SQL 2005 [SQL_Latin1_General_CP1_CI_AS]から2008への移行-「後方互換性」を使用すると、機能が失われますか

SQL 2005 [インスタンスとDBの照合順序はSQL_Latin1_General_CP1_CI_AS]からSQL 2008 [デフォルトはLatin1_General_CI_AS]に移行します。

SQL 2008 R2のインストールを完了し、デフォルトのLatin1_General_CI_AS照合を使用しましたが、データベースの復元はSQL_Latin1_General_CP1_CI_ASのままです。例外的な問題が発生しました-dbがLatin1_General_CI_ASにあったときに#tempテーブルがSQL_Latin1_General_CP1_CI_ASにあり、ここが私が今いる場所です-落とし穴に関するアドバイスが必要です。

SQL 2008 R2のインストール時に、'SQL Collation, used for backwards compatibility'を使用するオプションがあります。2005データベースと同じ照合を選択するオプションがあります:SQL_Latin1_General_CP1_CI_AS

  1. これにより、#tempテーブルで問題が発生しなくなりますが、落とし穴はありますか?

  2. SQL 2008の "現在の"照合順序を使用しないと、機能や種類が失われますか?

  3. 2008年からSQL 2012に(たとえば2年間で)移動した場合はどうなりますか?それでは問題がありますか?
  4. いつかLatin1_General_CI_ASに行かざるを得ませんか?

  5. 一部のDBAのスクリプトが完全なデータベースの行を完了し、新しい照合を使用してデータベースに挿入スクリプトを実行することを読みました-非常に怖くてこれに警戒しています-これを行うことをお勧めしますか?

18
Peter PitLock

まず、照合、並べ替え順序、コードページなどの用語について人々が話し合うときは、依然として多くの混乱があると私は感じているので、このような長い回答に対する謝罪です。

From [〜#〜] bol [〜#〜] から:

SQL Serverの照合順序は、データの並べ替えルール、大文字と小文字、およびアクセントの区別のプロパティを提供します。 charやvarcharなどの文字データ型で使用される照合順序は、そのデータ型で表すことができるコードページと対応する文字を指定します。 SQL Serverの新しいインスタンスをインストールする場合でも、データベースバックアップを復元する場合でも、サーバーをクライアントデータベースに接続する場合でも、作業するデータのロケール要件、並べ替え順序、大文字と小文字、アクセントの区別を理解することが重要です。 。

これは、データの文字列を並べ替えて比較する方法に関する規則を指定する照合が非常に重要であることを意味します。

注: [〜#〜] collat​​ionproperty [〜#〜] に関する詳細情報

ここで、まず違いを理解しましょう……

T-SQLの下で実行:

_SELECT *
FROM::fn_helpcollations()
WHERE NAME IN (
        'SQL_Latin1_General_CP1_CI_AS'
        ,'Latin1_General_CI_AS'
        )
GO

SELECT 'SQL_Latin1_General_CP1_CI_AS' AS 'Collation'
    ,COLLATIONPROPERTY('SQL_Latin1_General_CP1_CI_AS', 'CodePage') AS 'CodePage'
    ,COLLATIONPROPERTY('SQL_Latin1_General_CP1_CI_AS', 'LCID') AS 'LCID'
    ,COLLATIONPROPERTY('SQL_Latin1_General_CP1_CI_AS', 'ComparisonStyle') AS 'ComparisonStyle'
    ,COLLATIONPROPERTY('SQL_Latin1_General_CP1_CI_AS', 'Version') AS 'Version'

UNION ALL

SELECT 'Latin1_General_CI_AS' AS 'Collation'
    ,COLLATIONPROPERTY('Latin1_General_CI_AS', 'CodePage') AS 'CodePage'
    ,COLLATIONPROPERTY('Latin1_General_CI_AS', 'LCID') AS 'LCID'
    ,COLLATIONPROPERTY('Latin1_General_CI_AS', 'ComparisonStyle') AS 'ComparisonStyle'
    ,COLLATIONPROPERTY('Latin1_General_CI_AS', 'Version') AS 'Version'
GO
_

結果は次のようになります。

enter image description here

上記の結果を見ると、唯一の違いは2つの照合順序の並べ替え順ですが、それは正しくありません。その理由は以下のとおりです。

テスト1:

_--Clean up previous query
IF OBJECT_ID('Table_Latin1_General_CI_AS') IS NOT NULL
    DROP TABLE Table_Latin1_General_CI_AS;

IF OBJECT_ID('Table_SQL_Latin1_General_CP1_CI_AS') IS NOT NULL
    DROP TABLE Table_SQL_Latin1_General_CP1_CI_AS;

-- Create a table using collation Latin1_General_CI_AS 
CREATE TABLE Table_Latin1_General_CI_AS (
    ID INT IDENTITY(1, 1)
    ,Comments VARCHAR(50) COLLATE Latin1_General_CI_AS
    )

-- add some data to it 
INSERT INTO Table_Latin1_General_CI_AS (Comments)
VALUES ('kin_test1')

INSERT INTO Table_Latin1_General_CI_AS (Comments)
VALUES ('Kin_Tester1')

-- Create second table using collation SQL_Latin1_General_CP1_CI_AS 
CREATE TABLE Table_SQL_Latin1_General_CP1_CI_AS (
    ID INT IDENTITY(1, 1)
    ,Comments VARCHAR(50) COLLATE SQL_Latin1_General_CP1_CI_AS
    )

-- add some data to it 
INSERT INTO Table_SQL_Latin1_General_CP1_CI_AS (Comments)
VALUES ('kin_test1')

INSERT INTO Table_SQL_Latin1_General_CP1_CI_AS (Comments)
VALUES ('Kin_Tester1')

--Now try to join both tables
SELECT *
FROM Table_Latin1_General_CI_AS LG
INNER JOIN Table_SQL_Latin1_General_CP1_CI_AS SLG ON LG.Comments = SLG.Comments
GO
_

テスト1の結果:

_Msg 468, Level 16, State 9, Line 35
Cannot resolve the collation conflict between "SQL_Latin1_General_CP1_CI_AS" and "Latin1_General_CI_AS" in the equal to operation.
_

上記の結果から、異なる照合順序の列の値を直接比較できないことがわかります。列の値を比較するには、COLLATEを使用する必要があります。

テスト2:

Erland Sommarskogが msdnに関するこの議論 で指摘しているように、主な違いはパフォーマンスです。

_--Clean up previous query
IF OBJECT_ID('Table_Latin1_General_CI_AS') IS NOT NULL
    DROP TABLE Table_Latin1_General_CI_AS;

IF OBJECT_ID('Table_SQL_Latin1_General_CP1_CI_AS') IS NOT NULL
    DROP TABLE Table_SQL_Latin1_General_CP1_CI_AS;

-- Create a table using collation Latin1_General_CI_AS 
CREATE TABLE Table_Latin1_General_CI_AS (
    ID INT IDENTITY(1, 1)
    ,Comments VARCHAR(50) COLLATE Latin1_General_CI_AS
    )

-- add some data to it 
INSERT INTO Table_Latin1_General_CI_AS (Comments)
VALUES ('kin_test1')

INSERT INTO Table_Latin1_General_CI_AS (Comments)
VALUES ('kin_tester1')

-- Create second table using collation SQL_Latin1_General_CP1_CI_AS 
CREATE TABLE Table_SQL_Latin1_General_CP1_CI_AS (
    ID INT IDENTITY(1, 1)
    ,Comments VARCHAR(50) COLLATE SQL_Latin1_General_CP1_CI_AS
    )

-- add some data to it 
INSERT INTO Table_SQL_Latin1_General_CP1_CI_AS (Comments)
VALUES ('kin_test1')

INSERT INTO Table_SQL_Latin1_General_CP1_CI_AS (Comments)
VALUES ('kin_tester1')
_

---両方のテーブルにインデックスを作成します

_CREATE INDEX IX_LG_Comments ON  Table_Latin1_General_CI_AS(Comments)
go
CREATE INDEX IX_SLG_Comments ON  Table_SQL_Latin1_General_CP1_CI_AS(Comments)
_

---クエリを実行します

_DBCC FREEPROCCACHE
GO
SELECT Comments FROM Table_Latin1_General_CI_AS WHERE Comments = 'kin_test1'
GO
_

---これは暗黙的な変換になります

enter image description here

---クエリを実行します

_DBCC FREEPROCCACHE
GO
SELECT Comments FROM Table_SQL_Latin1_General_CP1_CI_AS WHERE Comments = 'kin_test1'
GO
_

---これは[〜#〜] not [〜#〜] IMPLICIT変換します

enter image description here

暗黙的な変換の理由は、データベースとサーバーの照合順序が_SQL_Latin1_General_CP1_CI_AS_であり、テーブルTable_Latin1_General_CI_ASに列CommentsVARCHAR(50)として定義されているためです。 COLLATE Latin1_General_CI_ASを使用するため、ルックアップ中にSQL ServerはIMPLICIT変換を行う必要があります。

テスト3:

同じ設定で、varchar列をnvarchar値と比較して、実行プランの変更を確認します。

-クエリを実行します

_DBCC FREEPROCCACHE
GO
SELECT Comments FROM Table_Latin1_General_CI_AS WHERE Comments =  (SELECT N'kin_test1' COLLATE Latin1_General_CI_AS)
GO
_

enter image description here

-クエリを実行します

_DBCC FREEPROCCACHE
GO
SELECT Comments FROM Table_SQL_Latin1_General_CP1_CI_AS WHERE Comments = N'kin_test1'
GO
_

enter image description here

最初のクエリはインデックスシークを実行できますが、暗黙的な変換を実行する必要がありますが、2番目のクエリはインデックススキャンを実行するため、大きなテーブルをスキャンする場合のパフォーマンスの点で非効率的です。

結論:

  • 上記のすべてのテストは、正しい照合がデータベースサーバーインスタンスにとって非常に重要であることを示しています。
  • _SQL_Latin1_General_CP1_CI_AS_は、ユニコードと非ユニコードのデータを並べ替えることができるルールが異なるSQL照合です。
  • SQL照合では、Unicodeデータと非Unicodeデータを比較するときにIndexを使用できません。上記のテストでは、nvarcharデータとvarcharデータを比較するときに、インデックススキャンを実行してシークしません。
  • _Latin1_General_CI_AS_は、ユニコードのデータと非ユニコードのデータを同じに並べ替えることができるルールを備えたWindows照合です。
  • Windowsの照合では、Unicodeデータと非Unicodeデータを比較するときに引き続きインデックス(上記の例ではインデックスシーク)を使用できますが、パフォーマンスがわずかに低下します。
  • Erland Sommarskogの回答と、彼が指摘した接続項目を読むことを強くお勧めします。

これにより、#tempテーブルで問題が発生しなくなりますが、落とし穴はありますか?

上記の私の答えを参照してください。

SQL 2008の "現在の"照合順序を使用しないと、機能や種類が失われますか?

それはすべて、あなたが参照している機能/機能に依存します。照合順序は、データの格納と並べ替えです。

2008年からSQL 2012に(たとえば2年間で)移動した場合はどうなりますか?それでは問題がありますか?ある時点で、Latin1_General_CI_ASに移動する必要がありますか?

カントバウチ!状況は変化する可能性があり、Microsoftの提案に沿っていると常に良いので、データと上記で述べた落とし穴を理解する必要があります。 this および this 接続アイテムも参照してください。

一部のDBAのスクリプトが完全なデータベースの行を完了し、新しい照合を使用してデータベースに挿入スクリプトを実行することを読みました-非常に怖くてこれに警戒しています-これを行うことをお勧めしますか?

照合順序を変更する場合は、このようなスクリプトが役立ちます。データベースの照合順序をサーバーの照合順序と一致するように何度も変更していることに気付き、それをうまく行うスクリプトをいくつか用意しました。必要な場合はお知らせください。

参照:

20
Kin Shah

@ -Kinが answer で詳しく説明したものに加えて、サーバー(インスタンス)のデフォルトの照合を切り替えるときに注意すべきことがいくつかあります(水平線より上の項目は2つに直接関連しています)質問で言及された照合順序;水平線の下の項目は、一般に関連しています):

  • データベースのデフォルトのコレクションIS[〜#〜] NOT [〜#〜]CHANGINGの場合、「暗黙的な変換」のパフォーマンスの問題は、文字列リテラルとローカル変数はサーバーではなくデータベースのデフォルトの照合順序を使用するため、@ Kinの答えはでないが問題になるはずです。インスタンスレベルの照合順序が変更されるシナリオに対する唯一の影響ただし、データベースレベルの照合順序はありません(両方とも以下で詳しく説明します)。

    • 潜在的な照合は一時テーブルと競合します(テーブル変数は競合しません)。
    • 変数やカーソルの大文字と小文字が宣言と一致しない場合、コードが壊れる可能性があります(ただし、これは、バイナリまたは大文字と小文字を区別する照合を使用してインスタンスに移動した場合にのみ発生します)。
  • これら2つの照合順序の違いの1つは、VARCHARデータの特定の文字を並べ替える方法にあります(これはNVARCHARデータには影響しません)。 EBCDIC以外のSQL_照合順序は、VARCHARデータに対して「文字列ソート」と呼ばれるものを使用しますが、他のすべての照合順序、およびEBCDIC以外のSQL_照合順序に対してはNVARCHARデータでさえ、「ワードソート」。違いは、「単語の並べ替え」では、ダッシュ-とアポストロフィ'(およびおそらく他のいくつかの文字?)の重みが非常に低く、文字列に他の違いがない限り基本的に無視されることです。この動作を実際に確認するには、次のコマンドを実行します。

    DECLARE @Test TABLE (Col1 VARCHAR(10) NOT NULL);
    INSERT INTO @Test VALUES ('aa');
    INSERT INTO @Test VALUES ('ac');
    INSERT INTO @Test VALUES ('ah');
    INSERT INTO @Test VALUES ('am');
    INSERT INTO @Test VALUES ('aka');
    INSERT INTO @Test VALUES ('akc');
    INSERT INTO @Test VALUES ('ar');
    INSERT INTO @Test VALUES ('a-f');
    INSERT INTO @Test VALUES ('a_e');
    INSERT INTO @Test VALUES ('a''kb');
    
    SELECT * FROM @Test ORDER BY [Col1] COLLATE SQL_Latin1_General_CP1_CI_AS;
    -- "String Sort" puts all punctuation ahead of letters
    
    SELECT * FROM @Test ORDER BY [Col1] COLLATE Latin1_General_100_CI_AS;
    -- "Word Sort" mostly ignores dash and apostrophe
    

    戻り値:

    String Sort
    -----------
    a'kb
    a-f
    a_e
    aa
    ac
    ah
    aka
    akc
    am
    ar
    

    そして:

    Word Sort
    ---------
    a_e
    aa
    ac
    a-f
    ah
    aka
    a'kb
    akc
    am
    ar
    

    「文字列ソート」動作を「失う」ことになりますが、それを「機能」と呼ぶかどうかはわかりません。これは、望ましくないと見なされた動作です(Windows照合順序のいずれにも持ち込まれなかったという事実によって証明されています)。ただし、2つの照合順序間の動作の明確な違い(これも、EBCDIC以外のVARCHARデータの場合のみ)であり、コードや「文字列ソート」動作に基づく顧客の期待。 これには、コードをテストし、場合によってはこの動作の変更がユーザーに悪影響を与える可能性があるかどうかを調査する必要があります。

  • SQL_Latin1_General_CP1_CI_ASLatin1_General_100_CI_ASのもう1つの違いは、_VARCHARデータ(NVARCHAR dataは、SQL_の処理など、ほとんどのæ照合順序でこれらを実行できる) Expansions を実行する機能ですaeの場合:

    IF ('æ' COLLATE SQL_Latin1_General_CP1_CI_AS =
        'ae' COLLATE SQL_Latin1_General_CP1_CI_AS)
    BEGIN
      PRINT 'SQL_Latin1_General_CP1_CI_AS';
    END;
    
    IF ('æ' COLLATE Latin1_General_100_CI_AS =
        'ae' COLLATE Latin1_General_100_CI_AS)
    BEGIN
      PRINT 'Latin1_General_100_CI_AS';
    END;
    

    戻り値:

    Latin1_General_100_CI_AS
    

    ここで「失われている」唯一のことは、これらの拡張を実行できないことができないことです。一般的に言えば、これはWindows照合に移行するもう1つの利点です。ただし、「文字列の並べ替え」から「単語の並べ替え」への移動と同じように、同じ注意が適用されます。これは、2つの照合順序間の動作の明確な違いです(ここでも、VARCHARデータの場合のみ)。これらのマッピングを持つnotに基づいてコードおよび/または顧客の期待を持っている。 これには、コードをテストし、場合によってはこの動作の変更がユーザーに悪影響を与える可能性があるかどうかを調査する必要があります。

    (@ZarephethによるこのS.O.の回答で最初に言及された: SQL Server SQL_Latin1_General_CP1_CI_ASをLatin1_General_CI_ASに安全に変換できますか?

  • サーバーレベルの照合は、[model]を含むシステムデータベースの照合を設定するために使用されます。 [model]データベースは、サーバーを起動するたびに[tempdb]を含む新しいデータベースを作成するためのテンプレートとして使用されます。ただし、サーバーレベルの照合順序を変更して[tempdb]の照合順序を変更しても、CREATE #TempTableが実行されたときに「現在」になっているデータベースと[tempdb]の間の照合順序の違いを修正する簡単な方法があります。一時テーブルを作成するときは、COLLATE句を使用して照合を宣言し、DATABASE_DEFAULTの照合を指定します。

    CREATE TABLE #Temp (Col1 NVARCHAR(40) COLLATE DATABASE_DEFAULT);
    

  • 複数のバージョンが利用可能な場合は、目的の照合の最新バージョンを使用することをお勧めします。 SQL Server 2005以降、 "90"シリーズの照合順序が導入され、SQL Server 2008では "100"シリーズの照合順序が導入されました。これらの照合順序は、次のクエリを使用して見つけることができます。

    SELECT * FROM sys.fn_helpcollations() WHERE [name] LIKE N'%[_]90[_]%'; -- 476
    
    SELECT * FROM sys.fn_helpcollations() WHERE [name] LIKE N'%[_]100[_]%'; -- 2686
    

    SQL Server 2008 R2を使用しているため、Latin1_General_100_CI_ASではなくLatin1_General_CI_ASを使用する必要があります。

  • これらの特定の照合順序の大文字と小文字を区別するバージョン(つまり、SQL_Latin1_General_CP1_CS_ASLatin1_General_100_CS_AS)の違いは、大文字と小文字を区別する並べ替えを行う場合の大文字と小文字の順序です。これは、LIKE演算子およびPATINDEX関数で使用できる単一文字のクラス範囲(つまり、[start-end])にも影響します。次の3つのクエリは、並べ替えと文字範囲の両方でこの効果を示しています。

    SELECT tmp.col AS [Upper-case first]
    FROM (VALUES ('a'), ('A'), ('b'), ('B'), ('c'), ('C')) tmp(col)
    WHERE tmp.col LIKE '%[A-C]%' COLLATE SQL_Latin1_General_CP1_CS_AS
    ORDER BY tmp.col COLLATE SQL_Latin1_General_CP1_CS_AS; -- Upper-case first
    
    SELECT tmp.col AS [Lower-case first]
    FROM (VALUES ('a'), ('A'), ('b'), ('B'), ('c'), ('C')) tmp(col)
    WHERE tmp.col LIKE '%[A-C]%' COLLATE Latin1_General_100_CS_AS
    ORDER BY tmp.col COLLATE Latin1_General_100_CS_AS; -- Lower-case first
    
    SELECT tmp.col AS [Lower-case first]
    FROM (VALUES (N'a'), (N'A'), (N'b'), (N'B'), (N'c'), (N'C')) tmp(col)
    WHERE tmp.col LIKE N'%[A-C]%' COLLATE SQL_Latin1_General_CP1_CS_AS
    ORDER BY tmp.col COLLATE SQL_Latin1_General_CP1_CS_AS; -- Lower-case first
    

    小文字(同じ文字の場合)の前に大文字をソートする唯一の方法は、その動作をサポートする31の照合順序の1つを使用することです。これは、Hungarian_Technical_*照合順序と、SQL_の少数の照合順序(この動作のみをサポートする)です。 VARCHARデータ用)。

  • この特定の変更についてはそれほど重要ではありませんが、サーバーをバイナリまたは大文字と小文字を区別する照合に変更すると影響があるため、サーバーレベルの照合も以下に影響するということを知っておくとよいでしょう。

    • ローカル変数名
    • カーソル名
    • GOTOラベル
    • sysnameデータ型の名前解決


    つまり、すべての不良コードの責任があると思われるあなたまたは「最近去ったプログラマ」;-)が大文字小文字を区別せず、変数を@SomethingIDとして宣言したが、後で@somethingIdとして参照した場合、大文字と小文字を区別する照合またはバイナリ照合に移動すると、破損します。同様に、sysnameデータ型を使用しているが、それをSYSNAMESysName、またはすべて小文字以外のものとして参照しているコードも、大文字と小文字を区別する照合またはバイナリ照合。

5
Solomon Rutzky