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.
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 _BIN
hoặ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?
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ị ASCII
hoặc UNICODE
byte (tùy thuộc vào VARCHAR
hoặ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?
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 BIN2
thay 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ộ BIN2
sư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 Ordinal
tù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ộ _BIN2
sưu tập, tất nhiên).
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" ;-)).
Khi sử dụng dữ liệu Unicode (nghĩa là chuỗi có tiền tố N
hoặc đi vào SQL Server từ mã ứng dụng nơi kiểu dữ liệu đã được chỉ định là NChar
hoặ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 NCHAR
hoặc NVARCHAR
chuỗ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.
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 COLLATE
mệ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 BY
và 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
và _BIN2
) không phân biệt chữ hoa chữ thường !