Поддержка UTF-8, SQL Server 2012 и UTF8String UDT

Изучая плюсы и минусы SQL Server VARCHAR по сравнению с NVARCHAR для моего конкретного приложения, я пришел к выводу, что было бы идеально, если бы SQL Server изначально поддерживал UTF8. Несколько сообщений SO указывают, что это не так, например:

Является ли VARCHAR полностью похожим на 1990-е годы?

Каковы основные различия в производительности между типами данных SQL Server varchar и nvarchar?

Однако затем я наткнулся на эту статью в документации MSDN для SQL Server 2012, в которой показано, как создать определяемый пользователем тип данных UTF8String:

http://msdn.microsoft.com/en-us/library/ff877964(v=sql.110).aspx

Похоже, что UDT позволит использовать пространство (память, диск) в виде 8-битов на символ, будучи при этом достаточно гибким, чтобы хранить любую строку, которая может быть представлена ​​в UTF-8. Это правильно? Есть ли у этой стратегии недостатки (например, стоимость выполнения управляемого кода для каждой строки, ...)?


person Eric J.    schedule 24.01.2012    source источник


Ответы (1)


Создание настраиваемого определяемого пользователем типа с помощью SQLCLR не никоим образом не даст вам замены любого собственного типа. Это очень удобно для создания чего-либо для обработки специализированных данных. Но строки, даже с другой кодировкой, далеки от специализированных. Использование этого маршрута для ваших строковых данных приведет к снижению удобства использования вашей системы, не говоря уже о производительности, поскольку вы не сможете использовать какие-либо встроенные строковые функции.

Если бы вы могли сохранить что-либо на диске, эти выгоды были бы сведены на нет вашими потерями в общей производительности. Сохранение UDT осуществляется путем его сериализации в VARBINARY. Таким образом, чтобы выполнить любое сравнение строк ИЛИ сортировку, помимо "двоичного" / "порядкового" сравнения, вам нужно будет преобразовать все другие значения, одно за другим, обратно в UTF-8, чтобы затем сравнить строки, которые могут учитывать языковые различия. И это преобразование должно быть выполнено в UDT. Это означает, что, как и тип данных XML, вы должны создать UDT для хранения определенного значения, а затем предоставить метод этого UDT для принятия строкового параметра для сравнения (например, Utf8String.Compare(alias.field1) или, если вы определяете оператор для типа, затем Utf8string1 = Utf8string2 и пусть оператор = получит строку в кодировке UTF-8, а затем выполнит CompareInfo.Compare()).

В дополнение к приведенным выше соображениям вам также необходимо учитывать, что передача значений туда и обратно через SQLCLR API имеет определенную стоимость, особенно при использовании NVARCHAR(MAX) или VARBINARY(MAX), а не NVARCHAR(1 - 4000) и VARBINARY(1 - 4000) соответственно (пожалуйста, не путайте это различие как подразумевающее что-либо об использовании SqlChars / SqlBytes vs SqlString / SqlBinary).

Наконец (по крайней мере, с точки зрения использования UDT), пожалуйста, не упускайте из виду тот факт, что запрашиваемый UDT является образцом кода. Единственное отмеченное тестирование является чисто функциональным, ничего не связано с масштабируемостью или «уроками, извлеченными после работы с этим в течение года». Код функционального теста показан здесь, на следующей странице CodePlex, и его следует изучить, прежде чем переходить к этому решению, поскольку он дает представление о том, как вам нужно будет писать запросы, чтобы взаимодействовать с ним (что подходит для поля или два, но не для большинства / всех строковых полей):

http://msftengprodsamples.codeplex.com/SourceControl/latest#Kilimanjaro_Trunk/Programmability/CLR/UTF8String/Scripts/Test.sql

Было ли действительно сэкономлено место с учетом количества сохраненных вычисляемых столбцов и добавленных индексов? ;-)


Если речь идет о пространстве (диск, память и т. Д.), У вас есть три варианта:

  1. Если вы используете SQL Server 2008 или новее и используете Enterprise Edition, вы можете включить Сжатие данных. Сжатие данных может (но не всегда) сжимать данные Unicode в NCHAR и NVARCHAR полях. Определяющими факторами являются:

    1. NCHAR(1 - 4000) and NVARCHAR(1 - 4000) use the Standard Compression Scheme for Unicode, but only starting in SQL Server 2008 R2, AND only for IN ROW data, not OVERFLOW! This appears to be better than the regular ROW / PAGE compression algorithm.
    2. NVARCHAR(MAX) и XML (а также, я полагаю, VARBINARY(MAX), TEXT и NTEXT) данные, которые находятся В СТРОКЕ (не вне строки на страницах LOB или OVERFLOW), могут быть сжаты как минимум PAGE, и возможно также сжаты ROW ( не уверен насчет последнего).
    3. Любые данные OFF ROW, LOB или OVERLOW = Никакого сжатия для вас!
  2. Если вы используете версию старше 2008 года или нет в Enterprise Edition, у вас может быть два поля: одно VARCHAR и одно NVARCHAR. Например, предположим, что вы храните URL-адреса, которые в основном состоят из базовых символов ASCII (значения 0–127) и, следовательно, вписываются в VARCHAR, но иногда содержат символы Unicode. Ваша схема может включать следующие 3 поля:

      ...
      URLa VARCHAR(2048) NULL,
      URLu NVARCHAR(2048) NULL,
      URL AS (ISNULL(CONVERT(NVARCHAR([URLa])), [URLu])),
      CONSTRAINT [CK_TableName_OneUrlMax] CHECK (
                        ([URLa] IS NOT NULL OR [URLu] IS NOT NULL)
                    AND ([URLa] IS NULL OR [URLu] IS NULL))
    );
    

    В этой модели вы только ВЫБИРАЕТЕ из [URL] вычисляемого столбца. Для вставки и обновления вы определяете, какое поле использовать, видя, изменяет ли преобразование входящее значение, которое должно быть типа NVARCHAR:

    INSERT INTO TableName (..., URLa, URLu)
    VALUES (...,
            IIF (CONVERT(VARCHAR(2048), @URL) = @URL, @URL, NULL),
            IIF (CONVERT(VARCHAR(2048), @URL) <> @URL, NULL, @URL)
           );
    
  3. Если у вас есть поля, которые должны содержать только символы, подходящие для определенной кодовой страницы расширенного набора символов ASCII, просто используйте VARCHAR.


P.S. Просто для ясности: новые _SC Collations, представленные в SQL Server 2012, просто позволяют:

  • встроенные функции для правильной обработки дополнительных символов / суррогатных пар и
  • лингвистические правила для дополнительных символов, которые используются для упорядочивания и сравнения

Но даже без новых _SC Collations вы все равно можете сохранить любой символ Unicode в типе с префиксом XML или N и получить его без потери данных. Однако при использовании старых параметров сортировки (т.е. без номера версии в имени) все дополнительные символы приравниваются друг к другу. Вам нужно использовать _90 и _100 Collations, которые, по крайней мере, позволяют вам сравнивать и сортировать двоичные / кодовые точки; они не могут принимать во внимание лингвистические правила, поскольку не имеют конкретных отображений дополнительных символов (и, следовательно, не имеют весов или правил нормализации).

Попробуйте следующее:

IF (N'????' = N'????') SELECT N'????' AS [TheLiteral], NCHAR(150150) AS [Generated];
IF (N'????' = N'????') SELECT N'????' AS [TheLiteral], NCHAR(150151) AS [Generated];
IF (N'????' COLLATE Tatar_90_CI_AI = N'????' COLLATE Tatar_90_CI_AI)
       SELECT N'???? COLLATE Tatar_90_CI_AI' AS [TheLiteral], NCHAR(150151) AS [Generated];
IF (N'????' = N'?') SELECT N'?';

В БД, имеющей сопоставление по умолчанию, оканчивающееся на _SC, только первый оператор IF вернет набор результатов, а в поле «Создано» будут правильно отображаться символы.

Но если в БД нет сопоставления по умолчанию, заканчивающегося на _SC, и сопоставление не является сопоставлением серии _90 или _100, то первые два оператора IF возвращают наборы результатов, в которых поле «Создано» вернет NULL, а поле «Литерал» "поле отображается правильно.

Для данных Unicode сортировка не имеет отношения к физическому хранилищу.


ОБНОВЛЕНИЕ 2 октября 2018 г.

Хотя это еще не вариант, SQL Server 2019 предоставляет встроенную поддержку UTF-8 в VARCHAR / CHAR типах данных. В настоящее время в нем слишком много ошибок, чтобы его можно было использовать, но если они исправлены, то это вариант для некоторых сценариев. См. Мой пост "Встроенная поддержка UTF-8 в SQL Server 2019: Savior or False Prophet?" для подробного анализа этой новой функции.

person Solomon Rutzky    schedule 27.09.2015
comment
Что значит NVARCHAR(1 - 4000)? - person Eric J.; 27.09.2015
comment
@EricJ. Это означает, что нужно выбрать число от 1 до 4000. - person Aaron Bertrand; 27.09.2015
comment
@EricJ. Извините, если я не совсем понял это. В основном то, что сказал Аарон: это просто мой способ указать тип NVARCHAR, отличный от MAX, который может быть только в диапазоне от 1 до 4000. - person Solomon Rutzky; 28.09.2015