Сравнение одного «символа» (который может состоять из нескольких кодовых точек: суррогатных пар, объединения символов и т. Д.) Основано на довольно сложном наборе правил. Это настолько сложно из-за необходимости учитывать все различные (а иногда и «причудливые») правила, присутствующие во всех языках, представленных в спецификации Unicode . Эта система применяется к недвоичным сортировкам для всех NVARCHAR
данных и для VARCHAR
данных, использующих сортировку Windows, а не сортировку SQL Server (одна из которых начинается с SQL_
). Эта система не применяется к VARCHAR
данным, использующим параметры сортировки SQL Server, поскольку они используют простые сопоставления.
Большинство правил определены в Unicode Collation Algorithm (UCA) . Некоторые из этих правил, а некоторые не включены в UCA:
- Порядок / вес по умолчанию, указанный в
allkeys.txt
файле ( указан ниже)
- Какие параметры чувствительности и опции используются (например, чувствительны ли они к регистру или нечувствительны?, А если чувствительны, то в верхнем регистре или в нижнем регистре вначале?)
- Любые переопределения на основе локали.
- Версия стандарта Unicode используется.
- «Человеческий» фактор (т. Е. Unicode - это спецификация, а не программное обеспечение, и поэтому каждый поставщик может его реализовать)
Я подчеркнул, что последний момент, касающийся человеческого фактора, должен дать понять, что не следует ожидать, что SQL Server всегда будет вести себя на 100% в соответствии со спецификацией.
Основным фактором здесь является вес, заданный для каждой кодовой точки, и тот факт, что несколько кодовых точек могут использовать одну и ту же весовую спецификацию. Вы можете найти основные весы (без переопределений для конкретных локалей) здесь (я думаю, что 100
серия сопоставлений - это Unicode v 5.0 - неофициальное подтверждение в комментариях к элементу Microsoft Connect ):
http://www.unicode.org/Public/UCA/5.0.0/allkeys.txt
Кодовая точка, о которой идет речь, - U + FFFD - определяется как:
FFFD ; [*0F12.0020.0002.FFFD] # REPLACEMENT CHARACTER
Это обозначение определено в разделе 9.1 Формат файла Allkeys УЦА:
<entry> := <charList> ';' <collElement>+ <eol>
<charList> := <char>+
<collElement> := "[" <alt> <weight> "." <weight> "." <weight> ("." <weight>)? "]"
<alt> := "*" | "."
Collation elements marked with a "*" are variable.
Эта последняя строка важна, так как кодовая точка, на которую мы смотрим, имеет спецификацию, которая действительно начинается с "*". В разделе 3.6 «Взвешивание переменных» определены четыре возможных поведения, основанные на значениях конфигурации Collation, к которым у нас нет прямого доступа (они жестко запрограммированы в реализации Microsoft для каждого Collation, например, учитывает ли регистр символов сначала строчные буквы или сначала заглавными буквами, свойство, которое отличается между VARCHAR
данными, использующими SQL_
сопоставления и все другие варианты).
У меня нет времени на полное исследование того, какие пути выбраны, и на то, чтобы определить, какие варианты используются, чтобы дать более убедительные доказательства, но можно с уверенностью сказать, что в каждой спецификации Code Point, независимо от того, что-то или нет считается "равным" не всегда использовать полную спецификацию. В этом случае у нас есть «0F12.0020.0002.FFFD», и, скорее всего, используются только уровни 2 и 3 (т.е. .0020.0002. ). Выполнение «Count» в Notepad ++ для «.0020.0002». находит 12 581 совпадение (включая дополнительные символы, с которыми мы еще не имели дело). Выполнение «Count» для «[*» возвращает 4049 совпадений. Выполнение RegEx "Find" / "Count" с использованием шаблона\[\*\d{4}\.0020\.0002
возвращает 832 совпадений. Таким образом, где-то в этой комбинации плюс, возможно, некоторые другие правила, которые я не вижу, плюс некоторые подробности реализации, относящиеся к Microsoft, является полным объяснением этого поведения. И чтобы быть ясным, поведение одинаково для всех совпадающих символов, так как все они соответствуют друг другу, так как все они имеют одинаковый вес после применения правил (то есть, этот вопрос можно было задать по любому из них, не обязательно мистер �
).
С помощью приведенного ниже запроса и изменения COLLATE
предложения в соответствии с приведенными ниже результатами запроса можно увидеть, как работают различные функции чувствительности в двух версиях Collation:
;WITH cte AS
(
SELECT TOP (65536) ROW_NUMBER() OVER (ORDER BY (SELECT 0)) - 1 AS [Num]
FROM [master].sys.columns col
CROSS JOIN [master].sys.objects obj
)
SELECT cte.Num AS [Decimal],
CONVERT(VARBINARY(2), cte.Num) AS [Hex],
NCHAR(cte.Num) AS [Character]
FROM cte
WHERE NCHAR(cte.Num) = NCHAR(0xFFFD) COLLATE Latin1_General_100_CS_AS_WS --N'�'
ORDER BY cte.Num;
Различное количество совпадающих символов в разных сопоставлениях приведено ниже.
Latin1_General_100_CS_AS_WS = 5840
Latin1_General_100_CS_AS = 5841 (The "extra" character is U+3000)
Latin1_General_100_CI_AS = 5841
Latin1_General_100_CI_AI = 6311
Latin1_General_CS_AS_WS = 21,229
Latin1_General_CS_AS = 21,230
Latin1_General_CI_AS = 21,230
Latin1_General_CI_AI = 21,537
Во всех перечисленных выше сопоставлениях N'' = N'�'
также оценивается как истина.
ОБНОВИТЬ
Я смог сделать немного больше исследований, и вот что я нашел:
Как это "вероятно" должно работать
Используя демонстрацию сортировки ICU , я установил локаль на «en-US-u-va-posix», установил силу на «первичную», установил флажок «Показать ключи сортировки» и вставил следующие 4 символа, которые я скопировал из Результаты запроса выше (с использованием Latin1_General_100_CI_AI
сортировки):
�
Ԩ
ԩ
Ԫ
и это возвращает:
Ԫ
60 2E 02 .
Ԩ
60 7A .
ԩ
60 7A .
�
FF FD .
Затем проверьте свойства символа « » по адресу http://unicode.org/cldr/utility/character.jsp?a=fffd и убедитесь, что ключ сортировки уровня 1 (т. FF FD
Е.) Соответствует свойству «uca». Нажав на это свойство "uca", вы попадете на страницу поиска - http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3Auca%3DFFFD%3A%5D - и увидите только 1 совпадение. И в файле allkeys.txt вес сортировки уровня 1 отображается как 0F12
, и для этого есть только 1 совпадение.
Чтобы убедиться, что мы правильно интерпретируем поведение, я посмотрел на другого персонажа: ПИСЬМО ГРЕЧЕСКОГО КАПИТАЛА с VARIA Ὸ
на http://unicode.org/cldr/utility/character.jsp?a=1FF8, в котором есть «uca» ( т. е. уровень 1 сортирует вес / элемент сортировки) 5F30
. Нажав на это «5F30», вы попадете на страницу поиска - http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3Auca%3D5F30%3A%5D - показаны 30 совпадений, 20 из они находятся в диапазоне 0 - 65535 (то есть U + 0000 - U + FFFF). Глядя в файл allkeys.txt для Code Point 1FF8 , мы видим вес сортировки уровня 1 12E0
. Делать "Подсчет" в Notepad ++ на12E0.
показывает 30 совпадений (это совпадает с результатами Unicode.org, хотя это не гарантируется, поскольку файл предназначен для Unicode v 5.0, а сайт использует данные Unicode v 9.0).
В SQL Server следующий запрос возвращает 20 совпадений, аналогично поиску Unicode.org при удалении 10 дополнительных символов:
;WITH cte AS
(
SELECT TOP (65535) ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS [Num]
FROM [master].sys.columns col
CROSS JOIN [master].sys.objects obj
)
SELECT cte.Num AS [Decimal],
CONVERT(VARCHAR(50), CONVERT(VARBINARY(2), cte.Num), 2) AS [Hex],
NCHAR(cte.Num) AS [Character]
FROM cte
WHERE NCHAR(cte.Num) = NCHAR(0x1FF8) COLLATE Latin1_General_100_CI_AI
ORDER BY cte.Num;
И, просто чтобы быть уверенным, вернемся к демонстрационной странице сортировки ICU и заменим символы в поле «Ввод» следующими 3 символами, взятыми из списка 20 результатов из SQL Server:
Ὂ
𝜪
Ὸ
показывает, что все они имеют одинаковый 5F 30
вес сортировки уровня 1 (соответствует полю "uca" на странице свойств персонажа).
ТАК, конечно, кажется, что этот конкретный персонаж не должен соответствовать чему-либо еще.
Как это на самом деле работает (по крайней мере, в Microsoft-Land)
В отличие от SQL Server, в .NET есть средство отображения ключа сортировки для строки с помощью метода CompareInfo.GetSortKey . Используя этот метод и передавая только символ U + FFFD, он возвращает ключ сортировки 0x0101010100
. Затем, перебирая все символы в диапазоне от 0 до 65535, чтобы увидеть, какие из них имеют ключ сортировки, 0x0101010100
вернувший 4529 совпадений. Это не совсем совпадает с 5840, возвращаемым в SQL Server (при использовании Latin1_General_100_CS_AS_WS
Collation), но это самое близкое, что мы можем получить (на данный момент), учитывая, что я использую Windows 10 и .NET Framework версии 4.6.1, которая использует Unicode v 6.3.0 в соответствии с диаграммой для класса CharUnicodeInfo(в разделе «Примечание для абонентов», в разделе «Замечания»). На данный момент я использую функцию SQLCLR и поэтому не могу изменить целевую версию Framework. Когда у меня будет возможность, я создам консольное приложение и использую целевую версию Framework 4.5, которая использует Unicode v 5.0, которая должна соответствовать параметрам сортировки серии 100.
Этот тест показывает, что даже без точно такого же количества совпадений между .NET и SQL Server для U + FFFD совершенно ясно, что это не специфичное для SQL Server поведение и что это сделано преднамеренно или упущено при реализации Microsoft, символ U + FFFD действительно соответствует довольно многим символам, даже если он не соответствует спецификации Unicode. И, учитывая, что этот символ соответствует U + 0000 (ноль), это, вероятно, просто проблема недостающих весов.
ТАКЖЕ
Что касается различий в поведении в =
запросе по сравнению с LIKE N'%�%'
запросом, это связано с подстановочными знаками и отсутствующими (я предполагаю) весами для этих (то есть � Ƕ Ƿ Ǹ
) символов. Если LIKE
условие изменено на простое, LIKE N'�'
то оно возвращает те же 3 строки, что и =
условие. Если проблема с подстановочными знаками не связана с «отсутствующими» весами ( кстати, 0x00
ключ сортировки не возвращается CompareInfo.GetSortKey
, кстати), то это может быть связано с тем, что эти символы потенциально имеют свойство, позволяющее изменять ключ сортировки в зависимости от контекста (т. Е. Окружающие символы ).
FFFD
(поиск*0F12.0020.0002.FFFD
только возвращает один результат). Из наблюдения @ Forrest, что все они соответствуют пустой строке и немного больше чтения по теме, похоже, что вес, который они разделяют в различных недвоичных сопоставлениях, на самом деле я считаю нулевым.