Tác động hiệu năng Latin1_General_BIN khi thay đổi đối chiếu mặc định cơ sở dữ liệu


16

Tôi đã thiết lập đối chiếu cơ sở dữ liệu thành Latin1_General_BIN, để thực hiện so sánh chuỗi phân biệt chữ hoa chữ thường. Điều này sẽ có tác động đến hiệu suất? Nó có ảnh hưởng gì đến các hoạt động DML hoặc DDL trong cơ sở dữ liệu không? Cơ sở dữ liệu đã tồn tại với các bảng trong đó.

Câu trả lời:


24

Các bộ sưu tập trong SQL Server xác định các quy tắc khớp và sắp xếp dữ liệu ký tự. Thông thường, trước tiên bạn sẽ chọn đối chiếu dựa trên ngữ nghĩa so sánh và thứ tự sắp xếp mà người tiêu dùng dữ liệu yêu cầu.

Con người nói chung không thấy rằng các va chạm nhị phân tạo ra các hành vi sắp xếp và so sánh mà họ mong đợi. Vì vậy, mặc dù các tính năng này cung cấp hiệu suất tốt nhất (đặc biệt là các phiên bản BIN2 điểm mã thuần túy), hầu hết các triển khai không sử dụng chúng.

Tiếp theo trong các thuật ngữ hiệu suất thô (nhưng chỉ đối với các chuỗi không Unicode) là các đối chiếu SQL tương thích ngược . Khi làm việc với dữ liệu Unicode, các đối chiếu này sử dụng đối chiếu Windows thay thế, với cùng các đặc tính hiệu suất. Có những cái bẫy tinh tế ở đây, vì vậy bạn cần có lý do chính đáng để chọn đối chiếu SQL trong những ngày này (trừ khi làm việc trên một hệ thống của Hoa Kỳ, nơi nó vẫn là mặc định).

Các bộ sưu tập Windows là chậm nhất, nói chung, do các quy tắc sắp xếp và so sánh Unicode phức tạp. Tuy nhiên, những điều này cung cấp khả năng tương thích hoàn toàn với Windows trong SQL Server và thường xuyên được duy trì để theo kịp các thay đổi trong tiêu chuẩn Unicode. Để sử dụng hiện đại bao gồm dữ liệu Unicode, thường nên sử dụng đối chiếu Windows.

TL; DR

Nếu tất cả những gì bạn muốn là so sánh phân loại theo ngữ cảnh và phân loại theo ngữ cảnh, bạn nên chọn biến thể _CS_(đối với phân biệt chữ hoa chữ thường) của bất kỳ đối chiếu cơ sở nào cung cấp hành vi mong đợi cho ngôn ngữ và văn hóa của người dùng của bạn. Ví dụ: cả hai đều là các đối chiếu phân biệt chữ hoa chữ thường:

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

Bạn có thể xem các định nghĩa này bằng sys.fn_helpcollations

Ví dụ

Bốn bảng hoàn toàn giống nhau ngoại trừ đối chiếu; một nhị phân, một phân biệt chữ hoa chữ thường, một chữ hoa chữ thường và một chữ hoa chữ thường:

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
);

Dữ liệu mẫu giống nhau cho mỗi bảng:

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;

Bây giờ chúng tôi muốn tìm các chuỗi lớn hơn '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;

Các kết quả:

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

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

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

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

Cuối cùng ...

Tuy nhiên, xin lưu ý rằng, nếu chúng ta sử dụng một chữ Unicode với đối chiếu SQL, các quy tắc chuyển đổi ngầm định dẫn đến so sánh đối chiếu Windows:

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

... và kết quả đối chiếu SQL thay đổi :

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

10

Cho rằng đây là một cơ sở dữ liệu hiện có đã có các bảng được xác định trong đó, có một số ý nghĩa rất nghiêm trọng đối với hành động thay đổi đối chiếu cơ sở dữ liệu, vượt ra ngoài tác động hiệu năng tiềm năng đối với các hoạt động DML (thực tế đã có ở đó). Có tác động rất thực sự đến hiệu suất và chức năng và thay đổi này không những không đạt được mục tiêu dự định (ít nhất là không nhất quán), mà còn có khả năng thay đổi hành vi (hoặc sẽ thay đổi hành vi khi các bảng mới được tạo) về mặt dữ liệu được sắp xếp và đánh đồng như thế nào

Paul đã đưa ra lời giải thích tốt và các ví dụ về sự khác biệt trong hiệu suất và hành vi giữa các loại đối chiếu khác nhau trong câu trả lời của anh ấy, vì vậy tôi sẽ không nhắc lại ở đây. Tuy nhiên, một vài điểm cần một số chi tiết bổ sung và có một số điểm khác để thêm vào liên quan đến kịch bản hiện tại về việc thay đổi đối chiếu của một DB hiện tại, trái ngược với việc đặt đối chiếu của một DB mới.

  1. Collations nhị phân không chỉ là trường hợp nhạy cảm: chúng là tất cả mọi thứ nhạy cảm! Vì vậy, bằng cách sử dụng đối chiếu nhị phân (kết thúc bằng _BINhoặc _BIN2), các so sánh của bạn bây giờ cũng có dấu nhạy, nhạy cảm kana, nhạy chiều rộng và có khả năng nhạy cảm với gluten (ít nhất đó dường như là xu hướng ngày nay ;-)). Đây có phải là ảnh hưởng mong muốn của việc thực hiện thay đổi này? Là người dùng cuối mong đợi sự thay đổi hành vi này?

  2. Bộ sưu tập ảnh hưởng không chỉ so sánh, mà còn sắp xếp. Đối chiếu nhị phân sẽ sắp xếp dựa trên giá trị ASCIIhoặc UNICODEbyte (tùy thuộc vào VARCHARhoặc NVARCHAR, tương ứng) của mỗi byte . Do đó, bằng cách chọn đối chiếu nhị phân, bạn sẽ từ bỏ các quy tắc trọng số của ngôn ngữ / văn hóa cụ thể theo thứ tự từng ký tự (thậm chí các ký tự trong một số ngôn ngữ, chẳng hạn như tiếng Hungary, bao gồm 2 chữ cái) theo bảng chữ cái của văn hóa đó. Vì vậy, nếu "ch" tự nhiên xuất hiện sau "k", thì điều đó sẽ không xảy ra khi sử dụng đối chiếu nhị phân. Một lần nữa, đây có phải là ảnh hưởng mong muốn của việc thực hiện thay đổi này? Là người dùng cuối mong đợi sự thay đổi hành vi này?

  3. Trừ khi bạn có các yêu cầu tương thích ngược cụ thể cho ứng dụng của mình, bạn nên sử dụng BIN2thay vì BINđối chiếu, tất nhiên, giả sử rằng bạn muốn đối chiếu nhị phân ở vị trí đầu tiên. Các bộ BIN2sưu tập đã được giới thiệu trong SQL Server 2005 và theo trang MSDN cho Hướng dẫn sử dụng Bộ sưu tập BIN và BIN2 :

    Các đối chiếu nhị phân trước đó trong SQL Server, các kết thúc bằng "_BIN", đã thực hiện so sánh điểm-điểm-điểm-mã không hoàn chỉnh cho dữ liệu Unicode. Các đối chiếu nhị phân SQL Server cũ hơn đã so sánh ký tự đầu tiên là WCHAR, theo sau là so sánh theo từng byte.

    ...

    Bạn có thể di chuyển sang các đối chiếu nhị phân [_BIN2] để tận dụng các so sánh điểm mã thực sự và bạn nên sử dụng các đối chiếu nhị phân mới để phát triển các ứng dụng mới.

    Cũng cần lưu ý rằng các _BIN2đối chiếu phù hợp với hành vi của Ordinaltùy chọn của Bảng liệt kê StringComparison , do đó việc so sánh và sắp xếp được thực hiện trong mã .NET sử dụng tùy chọn đó sẽ mang lại kết quả tương tự như các hoạt động tương tự được thực hiện trong SQL Server (khi sử dụng các bộ _BIN2sưu tập, tất nhiên).

  4. Vì những lý do tương tự với những gì vừa được nêu về các _BIN2đối chiếu, trừ khi bạn có các yêu cầu cụ thể để duy trì hành vi tương thích ngược, bạn nên nghiêng về sử dụng các đối chiếu Windows chứ không phải các đối chiếu cụ thể của SQL Server (hiện tại các đối chiếu bắt đầu SQL_được xem xét hơi "sucky" ;-)).

  5. Khi sử dụng dữ liệu Unicode (nghĩa là chuỗi có tiền tố Nhoặc đi vào SQL Server từ mã ứng dụng nơi kiểu dữ liệu đã được chỉ định là NCharhoặc NVarChar), tôi không thấy cách sử dụng một đối chiếu so với khác sẽ tạo ra sự khác biệt khi chèn hoặc cập nhật trường NCHARhoặc NVARCHARchuỗi .

    Khi sử dụng dữ liệu không phải là Unicode, hoặc chèn vào hoặc cập nhật trường không phải là Unicode, thì đối chiếu cụ thể (cơ sở dữ liệu hoặc trường) có thể đóng một vai trò nhỏ nếu bất kỳ ký tự nào được chèn / cập nhật cần được dịch hoặc không thể ánh xạ (là thậm chí là một từ?), như được chỉ định bởi Trang Mã được xác định bởi đối chiếu. Tất nhiên, vấn đề tiềm ẩn này tồn tại bất cứ khi nào một người đang sử dụng dữ liệu hoặc loại dữ liệu không phải là Unicode và không cụ thể đối với kịch bản thay đổi đối chiếu DB này. Sự thay đổi đó sẽ tác động đến chuỗi ký tự (có thể đã là một vấn đề nếu đối chiếu DB khác với đối chiếu của trường). Nhưng ngay cả khi không có thay đổi nào đối với đối chiếu DB, dữ liệu đến từ các DB khác hoặc từ bên ngoài SQL Server (bất kỳ mã máy khách nào) có thể chứa bất kỳ ký tự nào và thuộc bất kỳ mã hóa cụ thể nào.

  6. RẤT QUAN TRỌNG!!! Khi thay đổi đối chiếu mặc định cơ sở dữ liệu, đối chiếu được chỉ định cho bất kỳ trường chuỗi hiện có nào trong bất kỳ bảng hiện có nào sẽ không thay đổi, nhưng bất kỳ trường mới nào cũng sẽ có đối chiếu cơ sở dữ liệu mặc định (trừ khi được ghi đè qua COLLATEmệnh đề). Điều này sẽ tác động đến các truy vấn của bạn theo ba cách:

    1) Nếu bất kỳ truy vấn nào THAM GIA trên bất kỳ trường nào trong số các trường hiện có với bất kỳ trường mới nào, bạn sẽ gặp lỗi không khớp đối chiếu:

    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;
    

    Trả về:

    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) Dự đoán / bộ lọc trên các trường hiện có của các bảng hiện có (được đặt thành đối chiếu mặc định trước đó) so sánh với chuỗi ký tự hoặc biến không bị lỗi, nhưng chúng chắc chắn có thể bị ảnh hưởng bởi hiệu suất do SQL Server cần đánh đồng đối chiếu cả hai bên và tự động chuyển đổi chuỗi ký tự hoặc biến thành đối chiếu của trường. Bật "Bao gồm kế hoạch thực hiện thực tế" (Control-M) và sau đó thực hiện các thao tác sau (giả sử rằng bạn đã chạy các truy vấn được hiển thị ở trên):

    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) VÀ, nói về chuyển đổi ngầm định, hãy chú ý cách nó là chuỗi ký tự (với đối chiếu ngụ ý của đối chiếu mặc định cơ sở dữ liệu Latin1_General_BIN2:) được chuyển đổi, không phải là trường trong bảng. Bất kỳ dự đoán nào về việc bộ lọc này sẽ không phân biệt chữ hoa chữ thường (đối chiếu cũ) hay phân biệt chữ hoa chữ thường (đối chiếu mới)? Chạy như sau để xem:

    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';
    

    Trả về:

    Col1
    ----
    a
    A
    

    Ôi! Do đó, không chỉ có một cú đánh hiệu suất nhẹ (hoặc có thể quan trọng hơn?) Cho truy vấn này CONVERT_IMPLICIT(), mà thậm chí nó còn không hoạt động theo cách phân biệt chữ hoa chữ thường.

    Ergo, nếu đối chiếu được thay đổi trên DB đã có bảng, thì có, cả chức năng VÀ hiệu năng đều bị ảnh hưởng.

    Nếu đối chiếu đang được đặt trên một DB mới, thì Paul đã giải thích điều đó bằng cách giải thích cách đối chiếu nhị phân, trong khi nhanh, có thể sẽ không sắp xếp theo cách mà người ta mong đợi hoặc mong muốn.


Cũng cần lưu ý rằng bạn luôn có thể chỉ định các đối chiếu cho mỗi điều kiện. Các đối chiếu khoản có thể được thêm vào WHEREđiều kiện, ORDER BYvà hầu hết bất cứ nơi nào chấp nhận một chuỗi.

Ví dụ 1 (điều kiện 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;

Trả về:

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

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

Ví dụ 2 (ĐẶT HÀNG B) NG):

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;

Trả về:

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

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

Ví dụ 3 (câu lệnh 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];

Trả về:

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

{nothing}

Ví dụ 4 (liên kết với tham số đầu vào hàm):

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/

Trả về:

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

Giá trị UCS-2 là 55.356 đúng một phần ở chỗ nó là giá trị đầu tiên trong hai giá trị trong "cặp thay thế". Nhưng trừ khi được _SCđối chiếu rõ ràng , UNICODE()hàm chỉ có thể xem mỗi ký tự là một giá trị byte kép và không biết cách xử lý đúng một cặp thay thế hai byte kép.


CẬP NHẬT

Ngay cả với tất cả các ví dụ ở trên, một khía cạnh của các so sánh Nhạy cảm thường bị bỏ qua và bị phủ nhận bởi các so sánh / đối chiếu nhị phân, là chuẩn hóa (thành phần và phân tách) là một phần của Unicode.

Ví dụ 5 (khi so sánh nhị phân là không case-sensitive):

Các so sánh phân biệt chữ hoa chữ thường cho phép kết hợp các ký tự, kết hợp với một ký tự khác, tạo thành một ký tự khác đã tồn tại dưới dạng một điểm mã Unicode khác. Các so sánh phân biệt chữ hoa chữ thường quan tâm đến ký tự hiển thị, không phải (các) điểm mã được sử dụng để tạo nó.

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

Trả về:

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

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

So sánh trường hợp thực tế cũng cho phép các ký tự rộng tương đương với các tương đương không rộng của chúng.

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];

Trả về:

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


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

Ergo:

Bộ sưu tập BINary ( _BIN_BIN2) không phân biệt chữ hoa chữ thường !

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.