다음을 통해 공유


포함된 데이터베이스 데이터 정렬

적용 대상:SQL ServerAzure SQL Managed Instance

대/소문자 구분, 악센트 구분 및 사용 중인 기본 언어 등 다양한 속성이 텍스트 데이터의 정렬 순서 및 같음 의미 체계에 영향을 줍니다. 이러한 사항은 데이터에 대한 데이터 정렬 선택을 통해 SQL Server에 전달됩니다. 데이터 정렬 자체에 대한 자세한 내용은 데이터 정렬 및 유니코드 지원을 참조하세요.

데이터 정렬은 사용자 테이블에 저장된 데이터뿐만 아니라 메타데이터, 임시 개체, 변수 이름 등 SQL Server에서 처리하는 모든 텍스트에 적용됩니다. 이러한 처리는 포함된 데이터베이스와 포함되지 않은 데이터베이스에서 상이합니다. 이 변경은 많은 사용자에게 영향을 주지 않지만 인스턴스 독립성과 균일성을 제공하는 데 도움이 됩니다. 그러나 이로 인해 일부 혼란이 발생할 수 있으며 포함된 데이터베이스와 포함되지 않은 데이터베이스 모두에 액세스하는 세션에 문제가 발생할 수 있습니다.

포함된 데이터베이스의 데이터 정렬 동작은 포함되지 않은 데이터베이스의 동작과 미묘하게 다릅니다. 이 동작은 일반적으로 인스턴스 독립성과 단순성을 제공하는 데 유용합니다. 일부 사용자는 특히 세션이 포함된 데이터베이스와 포함되지 않은 데이터베이스 모두에 액세스할 때 문제가 있을 수 있습니다.

이 문서에서는 변경 내용을 명확히 하고 변경으로 인해 문제가 발생할 수 있는 영역을 검사합니다.

참고 항목

Azure SQL Database의 경우 포함된 데이터베이스에 대한 데이터 정렬은 다릅니다. 데이터베이스 데이터 정렬 및 카탈로그 데이터 정렬은 데이터베이스 생성 시 설정할 수 있으며 업데이트할 수 없습니다. 데이터(COLLATE)에 대한 데이터 정렬 및 시스템 메타데이터 및 개체 식별자(CATALOG_COLLATION)에 대한 카탈로그 데이터 정렬을 지정합니다. 자세한 내용은 CREATE DATABASE를 참조하세요.

포함되지 않은 데이터베이스

모든 데이터베이스에는 기본 데이터 정렬이 있습니다(데이터베이스를 만들거나 변경할 때 설정할 수 있음). 이 데이터 정렬은 데이터베이스의 모든 메타데이터 및 데이터베이스 내의 모든 문자열 열에 대한 기본값에 사용됩니다. 사용자는 COLLATE 절을 사용하여 특정 열에 대해 다른 데이터 정렬을 선택할 수 있습니다.

예 1

예를 들어 베이징에서 작업하는 중이라면 중국어 데이터 정렬을 사용할 수 있습니다.

ALTER DATABASE MyDB
    COLLATE Chinese_Simplified_Pinyin_100_CI_AS;

이제 열을 만드는 경우 기본 데이터 정렬은 이 중국어 데이터 정렬이지만 원하는 경우 다른 데이터 정렬을 선택할 수 있습니다.

CREATE TABLE MyTable
(
    mycolumn1 NVARCHAR,
    mycolumn2 NVARCHAR COLLATE Frisian_100_CS_AS
);
GO

SELECT name, collation_name
FROM sys.columns
WHERE name LIKE 'mycolumn%';
GO

결과 집합은 다음과 같습니다.

name            collation_name
--------------- ----------------------------------
mycolumn1       Chinese_Simplified_Pinyin_100_CI_AS
mycolumn2       Frisian_100_CS_AS

비교적 간단해 보이지만 여기에는 몇 가지 문제가 발생합니다. 열의 데이터 정렬은 테이블이 만들어지는 데이터베이스에 따라 달라지므로 저장 tempdb되는 임시 테이블을 사용할 때 문제가 발생합니다. 일반적으로 데이터 정렬은 인스턴스의 tempdb 데이터 정렬과 일치하며 데이터베이스 데이터 정렬과 일치하지 않아도 됩니다.

예제 2

이전에 표시된 (중국어) 데이터베이스를 Latin1_General 정렬과 함께 인스턴스에서 사용하는 경우를 고려해 보십시오.

CREATE TABLE T1 (T1_txt NVARCHAR (MAX));
GO

CREATE TABLE #T2 (T2_txt NVARCHAR (MAX));
GO

언뜻 보기에 이러한 두 테이블은 스키마가 같은 것처럼 보이지만 데이터베이스의 데이터 정렬이 다르기 때문에 값은 호환되지 않습니다.

SELECT T1_txt, T2_txt
FROM T1
     INNER JOIN #T2
         ON T1.T1_txt = #T2.T2_txt;

결과 집합은 다음과 같습니다.

메시지 468, 수준 16, 상태 9, 줄 2

같은 연산에서 "Latin1_General_100_CI_AS_KS_WS_SC"과 "Chinese_Simplified_Pinyin_100_CI_AS" 간의 데이터 정렬 충돌을 해결할 수 없습니다.

임시 테이블을 명시적으로 데이터 정렬하여 이 문제를 해결할 수 있습니다. SQL Server를 사용하면 COLLATE 절의 DATABASE_DEFAULT 키워드를 제공하여 이 작업을 더 쉽게 수행할 수 있습니다.

CREATE TABLE T1 (T1_txt NVARCHAR (MAX));
GO

CREATE TABLE #T2 (T2_txt NVARCHAR (MAX) COLLATE DATABASE_DEFAULT);
GO

SELECT T1_txt, T2_txt
FROM T1
     INNER JOIN #T2
         ON T1.T1_txt = #T2.T2_txt;

이제 이 쿼리는 오류 없이 실행됩니다.

변수를 사용한 데이터 정렬 종속 동작도 확인할 수 있습니다. 다음 함수를 살펴보세요.

CREATE FUNCTION f (@x INT)
RETURNS INT
AS
BEGIN
    DECLARE @I AS INT = 1;
    DECLARE @İ AS INT = 2;
    RETURN @x * @i;
END

이 함수는 조금 특이한 함수입니다. 반환 절에서 @i는 대/소문자를 구분하는 정렬 방식에서는 @I이나 중 어느 하나에 바인딩할 수 없습니다. 대/소문자를 구분하지 않는 Latin1_General 데이터 정렬에서 @i@I에 바인딩하고 함수는 1를 반환합니다. 하지만 대/소문자를 구분하지 않는 Turkish 데이터 정렬에서 @i에 바인딩하고 함수는 2를 반환합니다. 이렇게 하면 서로 다른 데이터 정렬을 사용하는 인스턴스 간을 이동하는 데이터베이스에 큰 피해를 줄 수 있습니다.

포함된 데이터베이스

포함된 데이터베이스의 디자인 목표는 독립적인 데이터베이스를 만드는 것이므로 인스턴스 및 tempdb 데이터 정렬에 대한 종속성은 존재하지 않아야 합니다. 이러한 목표를 위해 포함된 데이터베이스에는 카탈로그 데이터 정렬이라는 개념이 도입되었습니다. 카탈로그 데이터 정렬은 시스템 메타데이터 및 임시 개체에 사용됩니다. 세부 정보는 다음과 같이 제공됩니다.

포함된 데이터베이스에서 카탈로그 데이터 정렬은 Latin1_General_100_CI_AS_WS_KS_SC. 이 데이터 정렬은 SQL Server의 모든 인스턴스에 포함된 모든 데이터베이스에 대해 동일하며 변경할 수 없습니다.

데이터베이스 데이터 정렬은 보존되지만 사용자 데이터의 기본값 데이터 정렬로만 사용됩니다. 기본값으로 데이터베이스 데이터 정렬은 model 데이터베이스 데이터 정렬과 같지만 포함되지 않은 데이터베이스와 마찬가지로 사용자가 CREATE 또는 ALTER DATABASE 명령을 통해 변경할 수 있습니다.

새 키워드 CATALOG_DEFAULTCOLLATE 절에서 사용할 수 있습니다. 이는 포함된 데이터베이스와 포함되지 않은 데이터베이스 모두에서 메타데이터의 현재 데이터 정렬에 대한 바로 가기로 사용됩니다. 즉, 포함되지 않은 데이터베이스에서는 데이터베이스 정렬에 메타데이터가 정렬되므로, CATALOG_DEFAULT는 현재 데이터베이스 정렬을 반환합니다. 포함된 데이터베이스에서는 사용자가 카탈로그 데이터 정렬과 일치하지 않도록 데이터베이스 데이터 정렬을 변경할 수 있으므로 이러한 두 값이 다를 수 있습니다.

포함되지 않은 데이터베이스와 포함된 데이터베이스 모두에서 다양한 개체의 동작은 다음 테이블에 요약되어 있습니다.

Item 포함되지 않은 데이터베이스 포함된 데이터베이스
사용자 데이터(기본값) DATABASE_DEFAULT DATABASE_DEFAULT
임시 데이터(기본값) tempdb 정렬 DATABASE_DEFAULT
메타데이터 DATABASE_DEFAULT / CATALOG_DEFAULT CATALOG_DEFAULT
임시 메타데이터 tempdb 정렬 CATALOG_DEFAULT
변수 인스턴스 데이터 정렬 CATALOG_DEFAULT
Goto 레이블 인스턴스 데이터 정렬 CATALOG_DEFAULT
커서 이름 인스턴스 데이터 정렬 CATALOG_DEFAULT

앞에서 설명된 임시 테이블 예를 보면 이 데이터 정렬 동작 때문에 대부분의 임시 테이블 사용 시 명시적 COLLATE 절이 필요하지 않다는 것을 알 수 있습니다. 포함된 데이터베이스에서 이 코드는 데이터베이스와 인스턴스 데이터 정렬이 다르더라도 이제 오류 없이 실행됩니다.

CREATE TABLE T1 (T1_txt NVARCHAR (MAX));
GO

CREATE TABLE #T2 (T2_txt NVARCHAR (MAX));
GO

SELECT T1_txt, T2_txt
FROM T1
     INNER JOIN #T2
         ON T1.T1_txt = #T2.T2_txt;

이 쿼리는 T1_txtT2_txt 모두 포함된 데이터베이스의 정렬 방식에 따라 정렬되었기 때문에 작동합니다.

포함된 컨텍스트와 포함되지 않은 컨텍스트 간 교차

포함된 데이터베이스에서 세션이 포함된 상태로 유지되는 만큼 연결된 데이터베이스 내에 남아 있어야 합니다. 이 경우 동작은 간단합니다. 그러나 세션이 포함된 컨텍스트와 포함되지 않은 컨텍스트 간에 교차하는 경우 두 규칙 집합이 브리지되어야 하므로 동작이 더 복잡해집니다. 부분적으로 포함된 데이터베이스 내에서 이러한 상황이 발생할 수 있으며, 사용자가 다른 데이터베이스에 연결할 수 있기 때문입니다 USE. 이 경우 데이터 정렬 규칙의 차이는 다음 원칙으로 처리됩니다.

  • 일괄 처리에 대한 데이터 정렬 동작은 일괄 처리가 시작되는 데이터베이스에 의해 결정됩니다.

이 결정은 초기 USE명령을 포함하여 명령을 실행하기 전에 결정됩니다. 즉, 포함된 데이터베이스에서 일괄 처리가 시작되지만 첫 번째 명령이 USE 포함되지 않은 데이터베이스에 대한 경우 포함된 데이터 정렬 동작은 일괄 처리에 계속 사용됩니다. 이 시나리오를 고려할 때 변수에 대한 참조는 다음과 같은 여러 가지 가능한 결과를 가질 수 있습니다.

  • 참조는 단 하나의 일치 항목을 찾을 수 있습니다. 이 경우 참조는 오류 없이 작동합니다.

  • 참조가 이전에 일치 항목을 찾았던 현재의 정렬 순서에서 일치하지 않을 수도 있습니다. 이렇게 하면 변수가 생성된 것으로 보이는 경우에도 변수가 존재하지 않음을 나타내는 오류가 발생합니다.

  • 참조는 원래 고유했던 여러 일치 항목을 찾을 수 있습니다. 또한 오류가 발생합니다.

몇 가지 예제를 사용하여 이를 보여 줍니다. 이 경우, 데이터 정렬이 기본 데이터 정렬로 설정되어 있는 부분적으로 포함된 데이터베이스인 MyCDB가 있다고 가정합니다 Latin1_General_100_CI_AS_WS_KS_SC. 인스턴스 데이터 정렬이 Latin1_General_100_CS_AS_WS_KS_SC라고 가정합니다. 두 데이터 정렬은 대/소문자 구분만 다릅니다.

예 1

다음 예에서는 참조가 일치하는 항목을 정확히 하나 찾는 경우를 보여 줍니다.

USE MyCDB;
GO

CREATE TABLE #a (x INT);

INSERT INTO #a VALUES (1);
GO

USE master;
GO

SELECT * FROM #a;
GO

Results:

결과 집합은 다음과 같습니다.

x
-----------
1

이 경우 식별된 #a는 대/소문자를 구분하지 않는 카탈로그 데이터 정렬과 대/소문자를 구분하는 인스턴스 데이터 정렬 모두에서 바인딩되며 코드가 작동합니다.

예제 2

다음 예제는 참조가 이전에는 일치 항목을 찾았으나 현재 데이터 정렬에서는 찾지 못하는 경우를 보여 줍니다.

USE MyCDB;
GO

CREATE TABLE #a (x INT);

INSERT INTO #A VALUES (1);
GO

여기서 #A는 대/소문자를 구분하지 않는 기본 데이터 정렬의 #a에 바인딩하고 삽입이 작동합니다.

결과 집합은 다음과 같습니다.

(1 row(s) affected)

그러나 스크립트를 계속 진행하면...

USE master;
GO

SELECT * FROM #A;
GO

대/소문자를 구분하는 인스턴스 데이터 정렬의 #A에 바인딩 하는 동안 오류가 발생합니다.

결과 집합은 다음과 같습니다.

Msg 208, Level 16, State 0, Line 2
Invalid object name '#A'.

예제 3

다음 예제에서는 참조가 원래는 고유했던 여러 일치 항목을 찾는 경우를 보여 줍니다. 먼저 tempdb(인스턴스와 대/소문자 구분 데이터 정렬이 동일)에서 시작하여 다음 문을 실행합니다.

USE tempdb;
GO

CREATE TABLE #a (x INT);
GO

CREATE TABLE #A (x INT);
GO

INSERT INTO #a VALUES (1);
GO

INSERT INTO #A VALUES (2);
GO

테이블은 이 데이터 정렬에서 고유하므로 이 쿼리는 성공합니다.

결과 집합은 다음과 같습니다.

(1 row(s) affected)
(1 row(s) affected)

그러나 포함된 데이터베이스로 이동하면 더 이상 이러한 테이블에 바인딩할 수 없습니다.

USE MyCDB;
GO

SELECT * FROM #a;
GO

결과 집합은 다음과 같습니다.

Msg 12800, Level 16, State 1, Line 2
The reference to temp table name #a is ambiguous and cannot be resolved. Possible candidates are #a and #A.