sql >> Base de Datos >  >> RDS >> Sqlserver

Compatibilidad con UTF-8, SQL Server 2012 y UTF8String UDT

Crear un tipo personalizado definido por el usuario a través de SQLCLR no , de ninguna manera, te va a conseguir un reemplazo de cualquier tipo nativo. Es muy útil para crear algo que maneje datos especializados. Pero las cadenas, incluso con una codificación diferente, están lejos de ser especializadas. Seguir esta ruta para sus datos de cadena destruiría cualquier cantidad de usabilidad de su sistema, sin mencionar el rendimiento, ya que no podría usar ninguna funciones de cadena integradas.

Si pudiera ahorrar algo en el espacio del disco, esas ganancias se borrarían por lo que perdería en el rendimiento general. El almacenamiento de un UDT se realiza serializándolo en un VARBINARY . Entonces, para hacer cualquier comparación de cadenas O clasificación, fuera de una comparación "binaria" / "ordinal", tendría que convertir todos los demás valores, uno por uno, de nuevo a UTF-8 para luego hacer la comparación de cadenas que puede dar cuenta de las diferencias lingüísticas. Y esa conversión tendría que hacerse dentro de la UDT. Esto significa que, al igual que el tipo de datos XML, crearía el UDT para contener un valor particular y luego expondría un método de ese UDT para aceptar un parámetro de cadena para hacer la comparación (es decir, Utf8String.Compare(alias.field1) o, si define un operador para el tipo, entonces Utf8string1 = Utf8string2 y tener el = el operador obtiene la cadena en la codificación UTF-8 y luego realiza CompareInfo.Compare() ).

Además de las consideraciones anteriores, también debe tener en cuenta que pasar valores de un lado a otro a través de la API SQLCLR tiene un costo, especialmente cuando se usa NVARCHAR(MAX) o VARBINARY(MAX) a diferencia de NVARCHAR(1 - 4000) y VARBINARY(1 - 4000) respectivamente (no confunda esta distinción con algo relacionado con el uso de SqlChars / SqlBytes vs SqlString / SqlBinary ).

Finalmente (al menos en términos de uso de un UDT), no pase por alto el hecho de que el UDT sobre el que se consulta es código de muestra . La única prueba observada es puramente funcional, nada relacionado con la escalabilidad o las "lecciones aprendidas después de trabajar con esto durante un año". El código de prueba funcional se muestra aquí en la siguiente página de CodePlex y debe revisarse antes de continuar con esta decisión, ya que da una idea de cómo necesitaría escribir sus consultas para interactuar con él (lo cual está bien para un campo o dos, pero no para la mayoría/todos los campos de cadena):

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

Dada la cantidad de columnas calculadas persistentes e índices agregados, ¿realmente se ahorró espacio?;-)

Cuando el espacio (disco, memoria, etc.) es la preocupación, tiene tres opciones:

  1. Si está utilizando SQL Server 2008 o más reciente y está en Enterprise Edition, puede habilitar Compresión de datos . La compresión de datos puede (pero no "siempre") comprimir datos Unicode en NCHAR y NVARCHAR campos. Los factores determinantes son:

    1. NCHAR(1 - 4000) y NVARCHAR(1 - 4000) use el Esquema de compresión estándar para Unicode , pero solo a partir de SQL Server 2008 R2, Y solo para datos IN ROW, ¡no DESBORDAMIENTO! Esto parece ser mejor que el algoritmo de compresión ROW / PAGE normal.
    2. NVARCHAR(MAX) y XML (y supongo que también VARBINARY(MAX) , TEXT y NTEXT ) los datos que están EN FILA (no fuera de fila en las páginas LOB o OVERFLOW) se pueden comprimir al menos en PÁGINA, y quizás también ROW comprimido (no estoy seguro de esto último).
    3. Cualquier dato OFF ROW, LOB o OVERLOW =¡Sin compresión para usted!
  2. Si usa una versión anterior a 2008 o no en Enterprise Edition, puede tener dos campos:uno VARCHAR y uno NVARCHAR . Por ejemplo, supongamos que está almacenando URL que en su mayoría son todos caracteres ASCII base (valores 0 - 127) y, por lo tanto, encajan en VARCHAR , pero a veces tienen caracteres Unicode. Su esquema puede incluir los siguientes 3 campos:

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

    En este modelo solo SELECCIONA del [URL] columna calculada. Para insertar y actualizar, determina qué campo usar al ver si la conversión altera el valor entrante, que debe ser de NVARCHAR tipo:

    INSERT INTO TableName (..., URLa, URLu)
    VALUES (...,
            IIF (CONVERT(VARCHAR(2048), @URL) = @URL, @URL, NULL),
            IIF (CONVERT(VARCHAR(2048), @URL) <> @URL, NULL, @URL)
           );
    
  3. Si tiene campos que solo deberían tener caracteres que se ajusten a una página de códigos particular de un conjunto de caracteres ASCII extendido, simplemente use VARCHAR .

PD Solo para aclarar esto:el nuevo _SC Las intercalaciones que se introdujeron en SQL Server 2012 simplemente permiten:

  • las funciones integradas para manejar correctamente los caracteres complementarios/pares sustitutos, y
  • reglas lingüísticas para caracteres complementarios que se utilizan para ordenar y comparar

Pero, incluso sin el nuevo _SC Intercalaciones, aún puede almacenar cualquier carácter Unicode en un XML o N -tipo con prefijo, y recuperarlo sin pérdida de datos. Sin embargo, cuando se utilizan intercalaciones más antiguas (es decir, sin número de versión en el nombre), todos los caracteres complementarios se equiparan entre sí. Necesitas usar el _90 y _100 Intercalaciones que al menos le brindan comparaciones y clasificaciones binarias / de puntos de código; no pueden tener en cuenta las reglas lingüísticas ya que no tienen asignaciones particulares de los caracteres complementarios (y, por lo tanto, no tienen pesos ni reglas de normalización).

Prueba lo siguiente:

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

En una base de datos que tiene una intercalación predeterminada que termina en _SC , solo el primer IF devolverá un conjunto de resultados y el campo "Generado" mostrará los caracteres correctamente.

Pero, si la base de datos no tiene una intercalación predeterminada que termine en _SC , y la intercalación no es un _90 o _100 intercalación de series, luego los dos primeros IF Las declaraciones devuelven conjuntos de resultados en los que el campo "Generado" devolverá NULL y el campo "Literal" se muestra correctamente.

Para los datos Unicode, la intercalación no influye en el almacenamiento físico.

ACTUALIZACIÓN 2018-10-02

Si bien esta aún no es una opción viable, SQL Server 2019 presenta soporte nativo para UTF-8 en VARCHAR / CHAR tipos de datos. Actualmente hay demasiados errores para usarlo, pero si se solucionan, entonces esta es una opción para algunos. escenarios. Consulte mi publicación, "Compatibilidad nativa con UTF-8 en SQL Server 2019:¿salvador o falso profeta? ", para un análisis detallado de esta nueva función.