Latin1_General_BIN dampak kinerja ketika mengubah susunan default basis data

16

Saya telah mengatur susunan basis data untuk Latin1_General_BIN, untuk membuat perbandingan string peka huruf. Apakah ini akan berdampak pada kinerja? Apakah akan berdampak pada operasi DML atau DDL dalam database? Basis data sudah ada dengan tabel di dalamnya.

Rakesh
sumber

Jawaban:

24

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

Contohnya

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;

Hasil:

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

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

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

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

Akhirnya...

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   
╚═════╝
Paul White Reinstate Monica
sumber
10

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.

  1. Binary collations lebih dari sekedar case-sensitive: mereka semuanya sensitif! Jadi, dengan menggunakan pemeriksaan biner (diakhiri dengan _BINatau _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?

  2. Kolasi memengaruhi tidak hanya perbandingan, tetapi juga penyortiran. Sebuah binary collation akan disortir berdasarkan nilai byte ASCIIatau UNICODE(tergantung pada VARCHARatau 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?

  3. Kecuali jika Anda memiliki persyaratan kompatibilitas-belakang khusus untuk aplikasi Anda, Anda harus menggunakan BIN2alih - alih BINpemeriksaan, dengan asumsi, tentu saja, bahwa Anda menginginkan pengumpulan biner di tempat pertama. Koleksi BIN2diperkenalkan 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 _BIN2kumpulan mudah cocok dengan perilaku Ordinalopsi 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 _BIN2collations, tentu saja).

  4. Untuk alasan yang serupa dengan apa yang baru saja dinyatakan mengenai _BIN2pengumpulan, 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" ;-)).

  5. Saat menggunakan data Unicode (mis. String diawali dengan Natau masuk ke SQL Server dari kode aplikasi tempat datatype ditentukan sebagai NCharatau NVarChar), saya tidak melihat bagaimana menggunakan satu collation vs yang lain akan membuat perbedaan untuk memasukkan atau memperbarui bidang string NCHARatau 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.

  6. 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 COLLATEklausa). 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_BIN2yang 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 WHEREkondisi, ORDER BYdan 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 _SCcollation, 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

Jadi:

BINARY ( _BINdan _BIN2) koleksi tidak Peka Huruf Besar-Kecil!

Solomon Rutzky
sumber