Вплив на продуктивність Latin1_General_BIN при зміні порівняння за замовчуванням у базі даних


16

Я встановив порівняння баз даних Latin1_General_BIN, щоб зробити порівняння рядків залежними від регістру. Чи матиме це вплив на продуктивність? Чи матиме це вплив на операції DML або DDL в базі даних? База даних вже існує з таблицями в ній.

Відповіді:


24

Збірники в SQL Server визначають правила відповідності та сортування даних символів. Зазвичай ви обираєте порівняння спочатку на основі семантики порівняння та порядку сортування, яких вимагають споживачі даних.

Люди, як правило, не знаходять бінарних порівнянь породжують поведінку сортування та порівняння, яку вони очікують. Отже, хоча вони забезпечують найкращу ефективність (особливо версії BIN2 з чистою кодовою точкою), більшість реалізацій не використовують їх.

Далі в умовах необробленої продуктивності (але лише для рядків, що не належать до Unicode) - це порівняння SQL -сумісності назад . Під час роботи з даними Unicode ці порівняння використовують замість Windows , з однаковими характеристиками продуктивності. Тут є тонкі пастки, тому вам потрібно мати вагомі причини для вибору порівняння SQL в ці дні (якщо не працювати в американській системі, де вона досі за замовчуванням).

Порівняння Windows є найповільнішим, загалом, через складні правила порівняння та сортування Unicode. Тим не менш, вони пропонують повну сумісність з Windows на SQL Server, і їх регулярно підтримують, щоб не відставати від змін у стандарті Unicode. Для сучасного використання, що включає дані Unicode, зазвичай рекомендується порівнювати Windows.

TL; DR

Якщо все, що вам потрібно, - це порівняльна та сортувальна семантика порівняння та сортування, слід вибрати _CS_варіант (для регістру, що враховує регістр) залежно від базового зіставлення, що забезпечує очікуване поведінку для мови та культури користувачів. Наприклад, обидва це залежно від регістру:

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

Ви можете переглянути ці визначення за допомогою sys.fn_helpcollations

Приклади

Чотири таблиці , які точно однакові, крім порівняння; один бінарний, один з урахуванням регістру, один нечутливий до регістру та один регістр SQL:

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

Одні зразкові дані для кожної таблиці:

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;

Тепер ми хочемо знайти рядки, більші за '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   
╚═════╝

Нарешті ...

Зауважте, що якщо ми використовуємо літерал Unicode при зіставленні SQL, то неявні правила перетворення призводять до порівняння порівняння Windows:

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

... і результати зіставлення SQL змінюються :

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

10

Враховуючи, що це вже наявна база даних якій вже визначені таблиці, є деякі дуже серйозні наслідки для дії зміни зібрання бази даних, окрім потенційного впливу на ефективність роботи DML (що насправді вже було там). Це дуже реально впливає на продуктивність та функціональність, і ця зміна не лише не досягла наміченої мети (принаймні не послідовно), але так само, як ймовірно, змінить поведінку (або змінить поведінку при створенні нових таблиць) з точки зору як упорядковуються та прирівнюються дані

У своїй відповіді Павло вже надав хороші пояснення та приклади відмінностей у виконанні та поведінці між різними типами порівнянь, тому я не повторюю це тут. Однак декілька пунктів потребують додаткової деталізації, і є кілька інших моментів, які слід додати стосовно поточного сценарію зміни складання існуючої БД, на відміну від встановлення зібрання нової БД.

  1. Бінарні порівняння є більш ніж чутливими до регістру: вони все чутливі! Отже, використовуючи двійкове порівняння (що закінчується на _BINабо _BIN2), ваші порівняння тепер також чутливі до акцентів, чутливих до кана, широкочутливих та потенційно чутливих до глютену (принаймні, це, здається, є тенденцією в ці дні ;-)). Чи це було бажаним впливом на внесення змін? Чи очікують кінцеві споживачі такої зміни поведінки?

  2. Збірники впливають не лише на порівняння, а й на сортування. Двійкове порівняння буде сортоване на основі значення ASCIIабо UNICODEбайта (залежно від VARCHARабо NVARCHAR, відповідно) кожного байту . Отже, вибираючи двійкове порівняння, ви відмовляєтесь від правил зважування, пов'язаних з мовою / культурою, які впорядковують кожного символу (навіть символи на якійсь мові, наприклад, угорській, які складаються з двох літер) відповідно до алфавіту цієї культури. Отже, якщо "ch" має, природно, настати після "k", то це не відбудеться за допомогою двійкового зіставлення. Знову ж, чи це було бажаним впливом на внесення змін? Чи очікують кінцеві споживачі такої зміни поведінки?

  3. Якщо ви не маєте конкретних вимог щодо сумісності у зворотному відношенні для свого додатка, вам слід використовувати BIN2замість BINпорівнянь, припускаючи, звичайно, що ви бажаєте в першу чергу двійкового порівняння. В BIN2сортування були введені в SQL Server 2005, а також в відповідно до сторінці MSDN для рекомендації по використанню BIN і BIN2 Collations :

    Попередні двійкові зіставлення в SQL Server, ті, що закінчуються на "_BIN", виконали неповне порівняння код-точка-код-точка для даних Unicode. Старі двійкові порівняння SQL Server порівнювали перший символ як WCHAR з подальшим порівнянням байт-байт.

    ...

    Ви можете перейти до бінарних зіставлень [_BIN2], щоб скористатися справжніми порівняннями кодових точок, і ви повинні використовувати нові бінарні порівняння для розробки нових програм.

    Слід також зазначити, що _BIN2зіставлення зручно відповідати поведінці Ordinalпараметра переліку StringComp poredation , таким чином, що порівняння та сортування, виконане в коді .NET, використовуючи цю опцію, дасть ті самі результати, що і ті самі операції, які виконуються в SQL Server (при використанні то _BIN2закуски, звичайно).

  4. З аналогічних причин, що були нещодавно заявлені щодо _BIN2порівнянь, якщо ви не маєте конкретних вимог щодо збереження зворотної сумісності, вам слід схилятися до використання порівнювань Windows, а не дотиків, що стосуються SQL Server (тобто тих, що починаються з цього часу SQL_, тепер вважаються своєрідно «задушливий» ;-)).

  5. Під час використання даних Unicode (тобто рядка з префіксом Nабо надходженням у SQL Server з коду програми, де тип даних вказано як NCharабо NVarChar), я не бачу, як використання одного зіставлення проти іншого змінило б значення для вставки або оновлення NCHARабо NVARCHARрядкового поля .

    Під час використання даних Unicode або вставки або оновлення поля, що не стосується Unicode, певне зіставлення (база даних чи поле) може зіграти невелику роль, якщо будь-які вставлені / оновлені символи потребують перекладу або не піддаються перегляду (є що навіть слово?), як зазначено на сторінці коду, яка визначається порівнянням. Звичайно, ця потенційна проблема існує всякий раз, коли використовується дані Unicode або типи даних, і не характерна для цього сценарію зміни зіставлення БД. Ця зміна вплине на рядкові літерали (що, можливо, вже було проблемою, якщо порівняння БД було іншим, ніж порівняння поля). Але навіть якщо в зіставленні БД не змінено ніяких змін, дані, що надходять з інших БД або з-за меж SQL Server (будь-який код клієнта), можуть містити будь-які символи та бути будь-якого конкретного кодування.

  6. ДУЖЕ ВАЖЛИВО!!! Під час зміни порівняння за замовчуванням у базі даних зіставлення, вказане для будь-яких існуючих рядкових полів у будь-яких існуючих таблицях, не зміниться, але будь-які нові поля матимуть порівняння за замовчуванням бази даних (якщо це не буде відмінено через COLLATEпункт). Це вплине на ваші запити трьома способами:

    1) Якщо будь-які запити ПРИЄДНУЙТЕСЯ з будь-якого з цих існуючих полів до будь-якого з нових полів, ви отримаєте помилку невідповідності зіставлення:

    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;

    Повернення:

    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) предикати / фільтри для існуючих полів існуючих таблиць (встановлених до попереднього зіставлення за замовчуванням), які порівнюються з рядковими літералами чи змінними, не помиляться, але, безумовно, це може вплинути на продуктивність через те, що SQL Server потребує прирівнювати порівняння обидві сторони і автоматично перетворює рядковий буквальний або змінний у зіставлення поля. Увімкніть "Включити фактичний план виконання" (Control-M), а потім виконати наступне (якщо припустити, що ви вже виконували запити, показані вище):

    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) І, кажучи про неявні перетворення, помітьте, як перетворюється рядковий літерал (з мається на увазі зіставлення бази даних за замовчуванням:) Latin1_General_BIN2, а не поле в таблиці. Будь-які здогадки щодо того, чи цей фільтр буде нечутливим до регістру (старий показник) чи чутливим до регістру (новий показник)? Виконайте наступне, щоб побачити:

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

    Повернення:

    Col1
    ----
    a
    A

    D'oh! Для цього запиту не тільки є незначна (а може, більш значна?) Ефективність CONVERT_IMPLICIT(), але вона навіть не веде себе бажаним чином, залежно від регістру.

    Ergo, якщо порівняння буде змінено на БД, у якій вже є таблиці, то так, і на продуктивність І функціональність впливають.

    Якщо порівняння встановлюється в новій БД, то Пол вже це пояснив, пояснивши, як двійкове порівняння, хоча воно швидке, напевно, не буде сортувати так, як можна було б очікувати чи бажати.


Слід також зазначити, що ви завжди можете вказати порівняння за умовою. Копіям пункт може бути доданий в WHEREумови, ORDER BYі найбільш будь-яке місце , яке приймає рядок.

Приклад 1 (умова, де):

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;

Повернення:

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

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

Приклад 2 (ЗАМОВИТИ ЗА):

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;

Повернення:

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

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

Приклад 3 (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];

Повернення:

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

{nothing}

Приклад 4 (асоціювати з вхідним параметром функції):

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/

Повернення:

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

Значення UCS-2 55356 частково правильне тим, що воно є першим із двох значень у "сурогатній парі". Але якщо явно не вказано _SCзіставлення, UNICODE()функція може бачити кожен символ лише як двобайтове значення і не знає, як правильно поводитись з подвійною двобайтовою сурогатною парою.


ОНОВЛЕННЯ

Навіть за всіма наведеними вище прикладами, одним із аспектів порівняння чутливих до випадків випадків, який зазвичай не помічається та заперечується бінарними порівняннями / зіставленнями, є нормалізація (склад та розкладання), що є частиною Unicode.

Приклад 5 (коли двійкове порівняння не відрізняється від регістру):

Справжнє порівняння з урахуванням регістру дозволяє поєднувати символи, які в поєднанні з іншим символом утворюють ще один символ, який вже існує як інша точка коду Unicode. У порівнянні з регістром порівняння стосуються відображуваного символу, а не кодових точок, які використовуються для його створення.

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

Повернення:

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

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

Справді порівняння з урахуванням регістру також дозволяють широким символам прирівнювати їх неширокі еквіваленти.

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

Повернення:

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


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

Ergo:

БІНАРІЙНІ ( _BINта _BIN2) порівняння не залежать від регістру!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.