Я установил параметры сортировки базы данных для Latin1_General_BIN
сравнения строк с учетом регистра. Повлияет ли это на производительность? Повлияет ли это на операции DML или DDL в базе данных? База данных уже существует с таблицами в ней.
Я установил параметры сортировки базы данных для Latin1_General_BIN
сравнения строк с учетом регистра. Повлияет ли это на производительность? Повлияет ли это на операции DML или DDL в базе данных? База данных уже существует с таблицами в ней.
Параметры сортировки в 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;
Теперь мы хотим найти строки больше, чем «а»:
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 ║
╚═════╝
Учитывая, что это существующая база данных которой уже определены таблицы, существуют некоторые очень серьезные последствия для действия по изменению параметров сортировки базы данных, помимо возможного влияния производительности на операции DML (которое фактически уже было там). Существует очень реальное влияние на производительность и функциональность, и это изменение не только не достигло намеченной цели (по крайней мере, непоследовательно), но с той же вероятностью изменило поведение (или изменит поведение при создании новых таблиц) с точки зрения как данные упорядочены и приравнены.
Пол уже дал хорошее объяснение и примеры различий в производительности и поведении между различными типами сопоставлений в своем ответе, поэтому я не буду повторять это здесь. Однако некоторые моменты требуют дополнительных подробностей, и есть несколько других моментов, которые необходимо добавить в связи с текущим сценарием изменения параметров сортировки существующей БД, в отличие от настройки параметров сортировки новой БД.
Двоичные сопоставления - это больше, чем просто регистр символов: они все чувствительны! Таким образом, с помощью двоичного сопоставления (оканчивающегося на _BIN
или _BIN2
) ваши сравнения теперь также чувствительны к акценту, кане, ширине и потенциально чувствительны к глютену (по крайней мере, сейчас это тренд ;-)). Был ли это желаемый эффект от внесения этих изменений? Ожидают ли конечные пользователи этого изменения поведения?
Сопоставления влияют не только на сравнения, но и на сортировку. Двоичная сортировка будет сортироваться на основе значения байта ASCII
или UNICODE
(в зависимости от VARCHAR
или NVARCHAR
соответственно) каждого байта . Следовательно, выбирая двоичное сопоставление, вы отказываетесь от весовых правил, специфичных для языка / культуры, которые упорядочивают каждый символ (даже символы в некотором языке, например, венгерском, которые состоят из 2 букв) в соответствии с алфавитом этой культуры. Так что, если «ch» естественно следует после «k», то это не произойдет при использовании двоичного сопоставления. Опять же, было ли это желаемым эффектом внесения этих изменений? Ожидают ли конечные пользователи этого изменения поведения?
Если у вас нет особых требований к обратной совместимости для вашего приложения, вы должны использовать BIN2
вместо BIN
сортировок, предполагая, конечно, что вы хотите двоичное сопоставление в первую очередь. Параметры BIN2
сортировки были введены в SQL Server 2005 и в соответствии со страницей MSDN « Рекомендации по использованию параметров сортировки BIN и BIN2» :
Предыдущие двоичные сопоставления в SQL Server, заканчивающиеся на «_BIN», выполняли неполное сравнение код-точка-точка для данных Unicode. В более ранних двоичных сопоставлениях SQL Server первый символ сравнивался как WCHAR с последующим побайтовым сравнением.
...
Вы можете перейти на двоичные сопоставления [_BIN2], чтобы воспользоваться преимуществами истинных сравнений кодовых точек, и вам следует использовать новые двоичные сопоставления для разработки новых приложений.
Следует также отметить, что параметры _BIN2
сортировки удобно соответствуют поведению Ordinal
параметра перечисления StringComparison , так что сравнения и сортировка, выполняемые в коде .NET с использованием этого параметра, будут давать те же результаты, что и те же операции, выполняемые в SQL Server (при использовании то _BIN2
закуски, конечно).
По тем же причинам, что были только что заявлены в отношении параметров _BIN2
сортировки, если у вас нет особых требований для поддержания поведения обратной совместимости, вы должны склоняться к использованию параметров сортировки Windows, а не специфических параметров SQL Server (то есть те, которые начинаются с SQL_
, теперь рассматриваются вроде "отстой" ;-)).
При использовании данных Unicode (т. Е. Строки с префиксом N
или входящей в SQL Server из кода приложения, в котором тип данных был указан как NChar
или NVarChar
), я не вижу, как использование одного сопоставления против другого может иметь значение для вставки или обновления поля NCHAR
или NVARCHAR
строки ,
При использовании данных не-Unicode или вставке или обновлении поля не-Unicode конкретное сопоставление (база данных или поле) может играть небольшую роль, если какие-либо вставляемые / обновляемые символы необходимо преобразовать или не отображать ( это даже слово?), как указано в кодовой странице, которая определяется сличением. Конечно, эта потенциальная проблема существует всякий раз, когда используются данные или типы данных, отличные от Unicode, и не относится к этому сценарию изменения параметров сортировки БД. Это изменение повлияет на строковые литералы (которые, возможно, уже были проблемой, если сортировка БД отличалась от сортировки поля). Но даже если никакие изменения не вносятся в параметры сортировки БД, данные, поступающие из других БД или извне SQL Server (любой клиентский код), могут содержать любые символы и иметь любую конкретную кодировку.
ОЧЕНЬ ВАЖНЫЙ!!! При изменении параметров сортировки по умолчанию базы данных, параметры сортировки, заданные для любых существующих строковых полей в любых существующих таблицах, не изменятся, но любые новые поля будут иметь параметры сортировки базы данных по умолчанию (если они не переопределены с помощью 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'о! Это не только незначительное (или, может быть, более значительное?) Снижение производительности для этого запроса из-за CONVERT_IMPLICIT()
, но он даже не ведет себя в нужном чувствительном к регистру виде.
Следовательно, если сортировка изменяется на БД, в которой уже есть таблицы, то да, это влияет как на производительность, так и на функциональность.
Если в новой БД устанавливается сопоставление, то Пол уже рассмотрел это, объяснив, как двоичное сопоставление, хотя и быстрое, вероятно, не будет сортироваться так, как можно было бы ожидать или желать.
Следует также отметить, что вы всегда можете указать параметры сортировки для каждого условия. Предложение COLLATE может быть добавлено к 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 (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;
Возвращает:
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, равное 55 356, является частично правильным, поскольку оно является первым из двух значений в «суррогатной паре». Но если явно не указано _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
_BIN
и _BIN2
) параметры сортировки не чувствительны к регистру!