SQL 2005 [SQL_Latin1_General_CP1_CI_AS]에서 2008로 이동- '이전 버전 호환성'을 사용하면 기능이 손실됩니다


18

우리는 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의 스크립트가 완전한 데이터베이스의 행을 완성한 다음 새 데이터 정렬을 사용하여 데이터베이스에 삽입 스크립트를 실행한다는 것을 읽었습니다.


2
SQL Server 2014에서 Hekaton에 액세스 할 수 있다고 생각되면 다음을 참조 하십시오 .
Aaron Bertrand

답변:


20

우선, 사람들이 데이터 정렬, 정렬 순서, 코드 페이지 등과 같은 용어에 대해 이야기 할 때 여전히 많은 혼란이 있다고 느끼기 때문에 그러한 긴 대답에 대해 사과드립니다.

에서 BOL :

SQL Server의 데이터 정렬은 데이터의 정렬 규칙, 대 / 소문자 구분 속성을 제공합니다 . char 및 varchar와 같은 문자 데이터 형식과 함께 사용되는 데이터 정렬은 코드 페이지와 해당 데이터 형식으로 나타낼 수있는 해당 문자를 나타냅니다. 새 SQL Server 인스턴스를 설치하거나 데이터베이스 백업을 복원하거나 서버를 클라이언트 데이터베이스에 연결하는 경우 작업 할 데이터의 로캘 요구 사항, 정렬 순서 및 대 / 소문자를 이해하는 것이 중요합니다. .

즉, 데이터 정렬이 데이터의 문자열을 정렬하고 비교하는 방법에 대한 규칙을 지정하므로 데이터 정렬이 매우 중요합니다.

참고 : COLLATIONPROPERTY에 대한 추가 정보

이제 차이점을 먼저 이해합시다 ......

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

결과는 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

위의 결과를 보면 유일한 차이점은 두 데이터 정렬 간의 정렬 순서입니다. 그러나 사실이 아니므로 다음과 같은 이유를 알 수 있습니다.

시험 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

--- 이것은 묵시적 ​​변환이있을 것입니다

여기에 이미지 설명을 입력하십시오

--- 쿼리를 실행

DBCC FREEPROCCACHE
GO
SELECT Comments FROM Table_SQL_Latin1_General_CP1_CI_AS WHERE Comments = 'kin_test1'
GO

--- 이것은 절대 변환 이 없습니다

여기에 이미지 설명을 입력하십시오

나는 같이 모두 내 데이터베이스 및 서버 데이터 정렬을 가지고 있기 때문에 암시 적 변환을위한 이유는 SQL_Latin1_General_CP1_CI_AS테이블의 Table_Latin1_General_CI_AS가 열이 댓글 로 정의 VARCHAR(50)COLLATE Latin1_General_CI_AS을 너무 SQL 서버는 암시 적 변환을 할 수있는 조회 중.

시험 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

여기에 이미지 설명을 입력하십시오

-쿼리를 실행

DBCC FREEPROCCACHE
GO
SELECT Comments FROM Table_SQL_Latin1_General_CP1_CI_AS WHERE Comments = N'kin_test1'
GO

여기에 이미지 설명을 입력하십시오

첫 번째 쿼리는 인덱스 검색을 수행 할 수 있지만 암시 적 변환을 수행해야하는 반면 두 번째 쿼리는 큰 테이블을 검색 할 때 성능 측면에서 비효율적 인 것으로 인덱스 검색을 수행합니다.

결론 :

  • 위의 모든 테스트에 따르면 데이터베이스 서버 인스턴스에 올바른 데이터 정렬이 매우 중요합니다.
  • SQL_Latin1_General_CP1_CI_AS 유니 코드와 비 유니 코드에 대한 데이터를 정렬 할 수있게하는 규칙이있는 SQL 데이터 정렬입니다.
  • nvarchar 데이터와 varchar 데이터를 비교할 때 인덱스 스캔을 수행하고 검색하지 않는 위의 테스트에서 볼 수 있듯이 유니 코드와 비 유니 코드 데이터를 비교할 때 SQL 데이터 정렬은 인덱스를 사용할 수 없습니다.
  • Latin1_General_CI_AS 유니 코드와 비 유니 코드에 대한 데이터를 정렬 할 수 있도록하는 규칙이 포함 된 Windows 데이터 정렬입니다.
  • 유니 코드 데이터와 비 유니 코드 데이터를 비교할 때 Windows 데이터 정렬은 여전히 ​​인덱스 (위 예제에서 인덱스 검색)를 사용할 수 있지만 약간의 성능 저하가 있습니다.
  • Erland Sommarskog 답변 + 그가 지적한 연결 항목을 읽는 것이 좋습니다.

이렇게하면 #temp 테이블에 문제가 없지만 함정이 있습니까?

위의 답변을 참조하십시오.

SQL 2008의 "현재"데이터 정렬을 사용하지 않으면 어떤 종류의 기능도 손실됩니까?

그것은 당신이 말하는 기능 / 기능에 달려 있습니다. 데이터 정렬은 데이터를 저장하고 정렬합니다.

2008 년에서 SQL 2012로 이전 할 때 (예 : 2 년)는 어떻습니까? 그렇다면 문제가 있습니까? 어느 시점에서 Latin1_General_CI_AS로 가야합니까?

캔트 바우처! 상황이 바뀔 수 있고 항상 Microsoft의 제안과 일치하는 것이 좋으므로 + 위에서 언급 한 데이터와 함정을 이해해야합니다. 또한 참조 연결 항목.

일부 DBA의 스크립트가 완전한 데이터베이스의 행을 완성한 다음 새 데이터 정렬을 사용하여 데이터베이스에 삽입 스크립트를 실행한다는 것을 읽었습니다.

데이터 정렬을 변경하려는 경우 이러한 스크립트가 유용합니다. 서버 데이터 정렬과 여러 번 일치하도록 데이터베이스 데이터 정렬을 변경하는 것을 발견했으며 꽤 깔끔한 스크립트가 있습니다. 필요한 경우 알려주세요.

참고 문헌 :


5

@Kin이 그의 답변 에서 자세히 설명한 것 외에도 서버 (예 : 인스턴스) 기본 데이터 정렬을 전환 할 때 알아야 할 사항이 더 있습니다 (가로선 위의 항목은 질문에 언급 된 두 데이터 정렬과 직접 관련이 있습니다. 수평선 아래는 일반과 관련이 있습니다.)

  • 데이터베이스의 기본 데이터 정렬 인 경우 NOT 변경 후 "암시 적 변환"성능 문제는 킨의 대답은해야 @ 설명 하지 문자열 리터럴 및 지역 변수는 데이터베이스의 기본 데이터 정렬이 아닌 서버를 사용하기 때문에 문제. 인스턴스 레벨 데이터 정렬이 변경되었지만 데이터베이스 레벨 데이터 정렬이 아닌 시나리오에 미치는 영향은 다음과 같습니다 (둘 다 자세히 설명 됨).

    • 잠재적 인 데이터 정렬이 임시 테이블과 충돌하지만 테이블 변수는 충돌하지 않습니다.
    • 변수 및 / 또는 커서의 대소 문자가 선언과 일치하지 않으면 코드가 손상 될 수 있습니다 (그러나 이진 또는 대소 문자 구분 데이터 정렬을 사용하여 인스턴스로 이동하는 경우에만 발생할 수 있음).
  • 이 두 데이터 정렬의 한 가지 차이점은 VARCHAR데이터의 특정 문자를 정렬하는 방법입니다 ( 데이터에 영향을 미치지 않음 NVARCHAR). 비 EBCDIC SQL_데이터 정렬은 VARCHAR데이터에 "문자열 정렬"이라는 것을 사용하는 반면, 다른 모든 데이터 정렬 및 NVARCHAR비 EBCDIC SQL_데이터 정렬에 대한 데이터 도 "워드 정렬"을 사용합니다. 차이점은 "워드 정렬"에서 대시 -와 아포스트로피 '(및 다른 문자 몇 개)는 가중치가 매우 낮으며 문자열에 다른 차이가없는 한 본질적으로 무시된다는 것입니다. 이 동작을 실제로 보려면 다음을 실행하십시오.

    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 데이터 정렬로 전달되지 않았다는 사실에 의해 입증 됨). 그러나, 그것은 이다 (다시, 단지 비 EBCDIC에 대한 두 개의 정렬 사이에 동작의 명확한 차이 VARCHAR데이터), 당신은 "문자열 정렬"행동을 기반으로 코드 및 / 또는 고객의 기대가있을 수 있습니다. 이를 위해서는 코드를 테스트하고 이러한 동작 변경이 사용자에게 부정적인 영향을 줄 수 있는지 조사해야합니다.

  • 또 다른 차이점 SQL_Latin1_General_CP1_CI_ASLatin1_General_100_CI_AS할 수있는 능력이다 확장이VARCHAR(데이터 NVARCHAR데이터가 이미 대부분이 할 수있는 SQL_등의 처리로, 데이터 정렬) æ가 것처럼 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 데이터 정렬로 이동하면 얻을 수있는 또 다른 이점입니다. 그러나 "문자열 정렬"에서 "워드 정렬"로 이동하는 것과 마찬가지로 동일한주의가 적용됩니다. 두 데이터 정렬 ( VARCHAR데이터에 대해서만) 사이의 동작에있어 명확한 차이 가 있으며 코드 및 / 또는 고객이있을 수 있습니다. 이러한 매핑이 없는 것을 기반으로 합니다. 이를 위해서는 코드를 테스트하고 이러한 동작 변경이 사용자에게 부정적인 영향을 줄 수 있는지 조사해야합니다.

    (@Zarepheth 가이 SO 답변에 처음 언급 한 : 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) 의 대소 문자 구분 버전의 차이점은 대소 문자 구분 정렬을 수행 할 때 대문자와 소문자 순서입니다. 또한 연산자 및 함수 와 [start-end]함께 사용할 수있는 단일 문자 클래스 범위 ( )에 영향을줍니다 . 다음 세 가지 쿼리는 정렬 및 문자 범위 모두에서이 효과를 보여줍니다.LIKEPATINDEX

    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 개 데이터 정렬 중 하나를 사용하는 것입니다 그 것이다 지원이 행동, Hungarian_Technical_*데이터 정렬과 소수의 SQL_단에 동작을 지원하는 데이터 정렬 ( VARCHAR데이터를 ).

  • 이 특정 변경에는 덜 중요하지만 서버를 이진 또는 대 / 소문자 구분 데이터 정렬로 변경하면 영향을 미치므로 서버 수준 데이터 정렬도 다음에 영향을 미칩니다.

    • 지역 변수 이름
    • 커서 이름
    • GOTO 라벨
    • sysname데이터 유형 의 이름 확인


    즉, 모든 잘못된 코드에 대해 책임이있는 사용자 또는 "최근에 떠난 프로그래머";-)는 대소 문자를 구분하지 않고 변수를 선언 @SomethingID했지만 @somethingId나중에 이를 변수로 선언 한 경우 사례로 이동하면 중단 될 수 있습니다. -민감 또는 이진 데이터 정렬. 마찬가지로, 사용하는 코드 sysname데이터 유형을하지만 그것을를 의미 SYSNAME, SysName소문자 구분 또는 이진 데이터 정렬을 사용하여 인스턴스로 이동하는 경우, 또는 모두 소문자 이외도 중단됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.