分享方式:


自主資料庫定序

適用於:SQL ServerAzure SQL 受控執行個體

許多屬性會影響文字資料的排序次序和相等語意,包括區分大小寫、區分腔調字和使用的基底語言。 這些特質會透過資料的定序選擇來傳達給 Microsoft SQL Server。 如需定序本身的更深入討論,請參閱 定序和 Unicode 支援

定序不只套用至儲存在使用者資料表中的資料,還會套用至 Microsoft SQL Server 所處理的所有文字,包括中繼資料、暫存物件、變數名稱等。在自主及非自主資料庫中,這些項目的處理方式會有所不同。 這項變更不會影響許多使用者,但有助於提供執行個體獨立性和一致性。 但這也可能會造成一些混淆,以及對於存取自主資料庫和非自主資料庫的連線階段造成問題。

自主資料庫的定序行為與非自主資料庫中的行為略有不同。 這種行為通常很有用,可提供執行個體獨立性和簡單性。 某些使用者可能會遇到問題,特別是當工作階段同時存取受控資料庫和非受控資料庫時。

本文釐清變更的內容,並檢查變更可能導致問題的區域。

注意

針對 Azure SQL 資料庫,自主資料庫的定序不同。 資料庫定序和目錄定序可以在建立資料庫時設定,且無法更新。 指定資料的定序 (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 透過為 DATABASE_DEFAULT 子句提供 COLLATE 關鍵字,使操作更加方便。

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

這是相當罕見的函數。 在區分大小寫的定序中,return 子句中的 @i 無法系結至 @I。 在不區分大小寫的 Latin1_General 定序中,@i 會繫結至 @I,而且函數會傳回 1。 但是,在不區分大小寫的土耳其文定序中,@i 會繫結至 ,而且函數會傳回 2。 這在具有不同定序之執行個體之間移動的資料庫上可能會造成破壞。

封閉式資料庫

因為自主資料庫的設計目標是要讓它們各自獨立,所以必須切斷執行個體和 tempdb 定序的相依性。 為了達到此目的,自主資料庫導入了目錄定序的概念。 目錄定序會用於系統中繼資料和暫時性物件。 詳細信息如下。

在自主資料庫中,目錄定序是 Latin1_General_100_CI_AS_WS_KS_SC。 在 SQL Server 的所有執行個體上,所有的內含資料庫的定序都相同,而且無法變更。

雖然資料庫定序會保留下來,不過只當做使用者資料的預設定序使用。 資料庫定序預設會等於 model 資料庫定序,但是使用者可透過 CREATEALTER DATABASE 命令進行變更,就如同非自主資料庫一樣。

CATALOG_DEFAULT 子句中會提供新關鍵字 COLLATE。 在包含和非自主資料庫中,這個關鍵字會當做中繼資料之目前定序的捷徑使用。 也就是說,在非包含的資料庫中,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.