Jawaban:
Collations di SQL Server menentukan aturan untuk mencocokkan dan mengurutkan data karakter. Biasanya, Anda akan memilih susunan pertama berdasarkan semantik perbandingan dan urutan penyortiran yang dibutuhkan konsumen data.
Manusia umumnya tidak menemukan bahwa biner menghasilkan perilaku penyortiran dan perbandingan yang mereka harapkan. Jadi, meskipun ini menawarkan kinerja terbaik (terutama versi BIN2 kode-titik murni) sebagian besar implementasi tidak menggunakannya.
Berikutnya dalam istilah kinerja mentah (tetapi hanya untuk string non-Unicode) adalah kumpulan SQL kompatibilitas mundur . Saat bekerja dengan data Unicode, susunan ini menggunakan susunan Windows , susunan ini menggunakan susunan sebagai gantinya, dengan karakteristik kinerja yang sama. Ada jebakan halus di sini, jadi Anda harus memiliki alasan yang baik untuk memilih susunan SQL hari ini (kecuali bekerja pada sistem AS, di mana itu masih default).
Windows collations adalah yang paling lambat, secara umum, karena perbandingan Unicode yang kompleks dan aturan penyortiran. Namun demikian, ini menawarkan kompatibilitas lengkap dengan Windows dalam SQL Server, dan secara teratur dipertahankan untuk mengikuti perubahan dalam standar Unicode. Untuk penggunaan modern yang menyertakan data Unicode, Windows collation umumnya direkomendasikan.
TL; DR
Jika semua yang Anda inginkan adalah perbandingan peka huruf besar-kecil dan semantik pengurutan, Anda harus memilih _CS_
variasi (untuk Peka Huruf Besar) dari mana susunan dasar mana pun yang menyediakan perilaku yang diharapkan untuk bahasa dan budaya pengguna Anda. Misalnya, keduanya adalah susunan peka huruf besar-kecil:
-- 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
Anda dapat melihat definisi ini menggunakan sys.fn_helpcollations
Empat tabel yang persis sama kecuali untuk pemeriksaan; satu biner, satu case-sensitive, satu case-insensitive, dan satu SQL case-sensitive:
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
);
Sama data sampel untuk setiap tabel:
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;
Sekarang kita mau menemukan string yang lebih besar dari '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;
╔═════╗
║ BIN ║
╠═════╣
║ b ║
║ c ║
╚═════╝
╔════╗
║ CS ║
╠════╣
║ A ║
║ b ║
║ B ║
║ c ║
║ C ║
╚════╝
╔════╗
║ CI ║
╠════╣
║ B ║
║ b ║
║ C ║
║ c ║
╚════╝
╔═════╗
║ SQL ║
╠═════╣
║ B ║
║ b ║
║ C ║
║ c ║
╚═════╝
Catatan, jika kita menggunakan Unicode literal dengan SQL collation, aturan konversi implisit menghasilkan perbandingan Windows collation:
SELECT ES.string AS SQL
FROM #Example_SQL AS ES
WHERE ES.string > N'a'
ORDER BY ES.string;
... dan hasil pemeriksaan SQL berubah :
╔═════╗
║ SQL ║
╠═════╣
║ A ║
║ B ║
║ b ║
║ C ║
║ c ║
╚═════╝
Mengingat bahwa ini adalah database yang ada yang sudah yang memiliki tabel yang ditentukan di dalamnya, ada beberapa implikasi yang sangat serius terhadap tindakan mengubah susunan database, di luar dampak kinerja potensial terhadap operasi DML (yang sebenarnya sudah ada di sana). Ada dampak yang sangat nyata untuk kinerja dan fungsionalitas, dan perubahan ini tidak hanya tidak mencapai tujuan yang diinginkan (setidaknya tidak secara konsisten), tetapi juga kemungkinan mengubah perilaku (atau akan mengubah perilaku ketika tabel baru dibuat) dalam hal bagaimana data disusun dan disamakan.
Paul sudah memberikan penjelasan dan contoh yang baik tentang perbedaan kinerja dan perilaku antara berbagai jenis pemeriksaan dalam jawabannya, jadi saya tidak akan mengulanginya di sini. Namun, beberapa poin memerlukan beberapa detail tambahan, dan ada beberapa poin lain untuk ditambahkan sehubungan dengan skenario saat ini mengubah susunan DB yang ada, yang bertentangan dengan pengaturan susunan DB baru.
Binary collations lebih dari sekedar case-sensitive: mereka semuanya sensitif! Jadi, dengan menggunakan pemeriksaan biner (diakhiri dengan _BIN
atau _BIN2
), perbandingan Anda sekarang juga peka aksen, peka kana, peka lebar, dan berpotensi peka terhadap gluten (setidaknya itulah yang tampaknya menjadi tren akhir-akhir ini ;-)). Apakah ini pengaruh yang diinginkan dari perubahan ini? Apakah pengguna akhir mengharapkan perubahan perilaku ini?
Kolasi memengaruhi tidak hanya perbandingan, tetapi juga penyortiran. Sebuah binary collation akan disortir berdasarkan nilai byte ASCII
atau UNICODE
(tergantung pada VARCHAR
atau NVARCHAR
, masing-masing) dari setiap byte . Oleh karena itu, dengan memilih susunan biner Anda melepaskan aturan pembobotan spesifik bahasa / budaya yang memerintahkan setiap karakter (bahkan karakter dalam beberapa bahasa, seperti Hongaria, yang terdiri dari 2 huruf) sesuai dengan alfabet budaya tersebut. Jadi, jika "ch" secara alami muncul setelah "k", yah, itu tidak akan terjadi dengan menggunakan biner biner. Lagi-lagi, apakah ini pengaruh yang diinginkan dari perubahan ini? Apakah pengguna akhir mengharapkan perubahan perilaku ini?
Kecuali jika Anda memiliki persyaratan kompatibilitas-belakang khusus untuk aplikasi Anda, Anda harus menggunakan BIN2
alih - alih BIN
pemeriksaan, dengan asumsi, tentu saja, bahwa Anda menginginkan pengumpulan biner di tempat pertama. Koleksi BIN2
diperkenalkan di SQL Server 2005, dan sesuai dengan halaman MSDN untuk Pedoman Penggunaan Koleksi BIN dan BIN2 :
Kumpulan biner sebelumnya di SQL Server, yang diakhiri dengan "_BIN", melakukan perbandingan kode-poin-ke-kode-poin yang tidak lengkap untuk data Unicode. Koleksi biner SQL Server yang lebih lama membandingkan karakter pertama sebagai WCHAR, diikuti oleh perbandingan byte-by-byte.
...
Anda dapat bermigrasi ke kumpulan biner [_BIN2] untuk mengambil keuntungan dari perbandingan titik kode yang benar, dan Anda harus menggunakan kumpulan biner baru untuk pengembangan aplikasi baru.
Juga harus dicatat bahwa _BIN2
kumpulan mudah cocok dengan perilaku Ordinal
opsi Enumerasi StringComparison , sehingga perbandingan dan pengurutan yang dilakukan dalam kode .NET menggunakan opsi itu akan menghasilkan hasil yang sama dengan operasi yang sama yang dilakukan dalam SQL Server (saat menggunakan yang _BIN2
collations, tentu saja).
Untuk alasan yang serupa dengan apa yang baru saja dinyatakan mengenai _BIN2
pengumpulan, kecuali jika Anda memiliki persyaratan khusus untuk mempertahankan perilaku kompatibilitas-mundur, Anda harus condong ke arah menggunakan kumpulan Windows dan bukan kumpulan khusus SQL Server (yaitu yang dimulai dengan SQL_
sekarang dianggap agak "sucky" ;-)).
Saat menggunakan data Unicode (mis. String diawali dengan N
atau masuk ke SQL Server dari kode aplikasi tempat datatype ditentukan sebagai NChar
atau NVarChar
), saya tidak melihat bagaimana menggunakan satu collation vs yang lain akan membuat perbedaan untuk memasukkan atau memperbarui bidang string NCHAR
atau NVARCHAR
.
Saat menggunakan data non-Unicode, atau menyisipkan ke dalam atau memperbarui bidang non-Unicode, maka susunan khusus (basis data atau bidang) mungkin memainkan peran kecil jika karakter apa pun yang dimasukkan / diperbarui perlu diterjemahkan, atau tidak dapat dipetakan (tidak itu bahkan sebuah kata?), sebagaimana ditentukan oleh Halaman Kode yang ditentukan oleh susunan. Tentu saja, masalah potensial ini ada setiap kali seseorang menggunakan data non-Unicode atau tipe data, dan tidak spesifik untuk skenario ini mengubah pengumpulan DB. Perubahan itu akan berdampak pada string literal (yang mungkin sudah menjadi masalah jika collation DB berbeda dari collation field). Tetapi bahkan jika tidak ada perubahan yang dilakukan pada susunan DB, data yang masuk dari DB lain atau dari luar SQL Server (kode klien apa pun) dapat berisi karakter apa saja dan dari penyandian tertentu.
SANGAT PENTING!!! Saat mengubah susunan default basis data, susunan yang ditentukan untuk bidang string apa pun yang ada di tabel apa pun yang ada tidak akan berubah, tetapi bidang baru apa pun akan memiliki susunan default basis data (kecuali ditimpa melalui COLLATE
klausa). Ini akan memengaruhi pertanyaan Anda dalam tiga cara:
1) Jika ada pertanyaan GABUNG di salah satu bidang yang ada ke salah satu bidang baru, Anda akan mendapatkan kesalahan ketidaksesuaian susunan:
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;
Pengembalian:
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) Predikat / filter pada bidang yang ada dari tabel yang ada (diatur ke susunan standar sebelumnya) yang dibandingkan dengan string literal atau variabel tidak akan kesalahan, tetapi mereka tentu saja akan terpengaruh oleh kinerja karena SQL Server perlu menyamakan susunan kedua sisi dan secara otomatis mengkonversi string literal atau variabel ke susunan bidang. Aktifkan "Sertakan Rencana Eksekusi Aktual" (Kontrol-M) dan kemudian jalankan yang berikut (dengan asumsi bahwa Anda sudah menjalankan kueri yang ditunjukkan di atas):
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) DAN, berbicara tentang konversi tersirat, perhatikan bagaimana string itu literal (dengan susunan tersirat dari susunan bawaan basis data:) Latin1_General_BIN2
yang dikonversi, bukan bidang dalam tabel. Adakah dugaan apakah filter ini peka terhadap huruf besar-kecil (kolasi lama) atau peka huruf besar-kecil (kolasi baru)? Jalankan yang berikut untuk melihat:
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';
Pengembalian:
Col1
----
a
A
Doh! Tidak hanya ada sedikit (atau mungkin lebih signifikan?) Hit kinerja untuk permintaan ini karenaCONVERT_IMPLICIT()
, tetapi bahkan tidak berperilaku dengan cara case-sensitive yang diinginkan.
Ergo, jika pemeriksaan diubah pada DB yang sudah memiliki tabel, maka ya, baik kinerja DAN fungsionalitas terpengaruh.
Jika susunan sedang diatur pada DB baru, maka Paul sudah membahasnya dengan menjelaskan bagaimana susunan biner, sementara cepat, mungkin tidak akan mengurutkan dengan cara yang diharapkan atau diinginkan seseorang.
Perlu juga dicatat bahwa Anda selalu dapat menentukan koleksi per-kondisi. The Collate klausul dapat ditambahkan ke WHERE
kondisi, ORDER BY
dan hampir semua tempat yang menerima string.
Contoh 1 (kondisi 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;
Pengembalian:
SQL-CaseSensitive
-----------------
b
B
Windows-CaseSensitive
-----------------
A
b
B
Contoh 2 (ORDER BY):
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;
Pengembalian:
Windows-CaseSensitive
-----------------
a
A
b
B
Windows-Binary
-----------------
A
B
a
b
Contoh 3 (pernyataan 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];
Pengembalian:
DatabaseDefault-CaseInsensitive?
--------------------------------
1
{nothing}
Contoh 4 (kaitkan dengan parameter input fungsi):
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/
Pengembalian:
UCS-2 UTF-16
------ -------
55356 127137
Nilai UCS-2 dari 55.356 sebagian benar karena itu adalah yang pertama dari dua nilai dalam "pasangan pengganti". Tetapi kecuali secara eksplisit diberi _SC
collation, UNICODE()
fungsi hanya dapat melihat setiap karakter sebagai nilai byte ganda dan tidak tahu bagaimana menangani dengan benar pasangan pengganti double-byte ganda.
MEMPERBARUI
Bahkan dengan semua contoh di atas, salah satu aspek perbandingan Case Sensitive yang biasanya diabaikan, dan dinegasi oleh perbandingan biner / collations, adalah normalisasi (komposisi dan dekomposisi) yang merupakan bagian dari Unicode.
Contoh 5 (ketika perbandingan biner tidak peka terhadap huruf besar-kecil):
Perbandingan case-sensitive yang sebenarnya memungkinkan untuk menggabungkan karakter yang, dalam kombinasi dengan karakter lain, membentuk karakter lain yang sudah ada sebagai titik kode Unicode lainnya. Perbandingan case-sensitive peduli tentang karakter yang dapat ditampilkan, bukan titik kode yang digunakan untuk membuatnya.
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
Pengembalian:
Binary ü u + combining diaeresis
------- --- -------------------------
{nothing}
Case-Sensitive ü u + combining diaeresis
--------------- --- -------------------------
Equal ü ü
Perbandingan case-sensitive yang sebenarnya juga memungkinkan karakter lebar untuk menyamakan dengan non-wide equivalents mereka.
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];
Pengembalian:
Binary
---------------
Values are different
Case-Sensitive
---------------
Values are the same
_BIN
dan _BIN2
) koleksi tidak Peka Huruf Besar-Kecil!