Traducción automática al convertir Unicode a no Unicode / NVARCHAR a VARCHAR

8

El punto de código Unicode 9619 es un carácter llamado "Sombra oscura": ( http://unicode-table.com/en/search/?q=9619 ).

Usando la SQL_Latin1_General_CP1_CI_AScolación y la página de códigos 1252, esperaría que convertir / convertir ese carácter Unicode en un tipo de datos que no sea Unicode resultaría en un signo de interrogación ( ?) ya que la página de códigos 1252 no parece contener este carácter y parece ser el SQL Server comportamiento cuando la conversión no puede tener lugar.

Entonces mi pregunta es: ¿por qué SQL Server convierte este carácter en un código ASCII 166 que es "Tubería, barra vertical rota" ¦:?

SELECT NCHAR(9619), CAST(NCHAR(9619) AS CHAR(1)), ASCII(CAST(NCHAR(9619) AS CHAR(1)))
Henry Lee
fuente
3
SQL Server utiliza lo que este documento llama transformación homoglífica y, a menudo, convierte caracteres que no se pueden representar en equivalentes cercanos. Como perder el acento de un personaje o cambiar las comillas inteligentes por comillas simples. ¡Sin embargo, estoy de acuerdo en que no se ve muy cerca! No estoy seguro de si estas transformaciones están documentadas o dónde.
Martin Smith
Wow, no tenía idea ... Dios, simplemente no parece correcto ... no es el mismo personaje. ¿Por qué no solo un "... oops, no se encuentra ese carácter en esta página de códigos ..." y falla la conversión?
Henry Lee
1
Solo leí esta página y recordé esto. No estoy seguro si SQL Server usa exactamente los mismos algoritmos de "mejor ajuste".
Martin Smith
1
@MartinSmith con respecto a no estar seguro de las asignaciones de "mejor ajuste" para SQL Server, consulte mi respuesta a continuación cuando encontré esas asignaciones :-).
Solomon Rutzky

Respuestas:

8

¿Por qué SQL convierte Unicode 9619 a código ASCII 166?

SQL Server no está empleando ninguna lógica personalizada especial aquí; está utilizando servicios estándar del sistema operativo para realizar la conversión.

Específicamente, el tipo de SQL Server y la expresión service ( sqlTsEs) llama a la rutina del sistema operativo WideCharToMultiByteen kernel32.dll. SQL Server establece los parámetros de entrada para WideCharToMultiByteque la rutina realice una 'traducción rápida'. Esto es más rápido que solicitar que se use un carácter predeterminado específico cuando no existe una traducción directa.

La traducción rápida se basa en la página de códigos de destino para realizar un mapeo que mejor se ajuste a los caracteres no coincidentes , como se menciona en el enlace que Martin Smith proporcionó en un comentario a la pregunta:

Las estrategias de mejor ajuste varían para diferentes páginas de códigos, y no están documentadas en detalle.

Cuando los parámetros de entrada se configuran para una traducción rápida, WideCharToMultiBytellama al servicio del sistema operativo GetMBNoDefault( fuente ). La inspección de la pila de llamadas de SQL Server al realizar la conversión especificada en la pregunta confirma esto:

SQL Server stack trace

Paul White 9
fuente
7

La conversión de datos Unicode a una página de códigos particular emplea lo que se conoce como la estrategia de "Mejor ajuste" (como se señala en la respuesta de @ Paul y en el enlace que @Martin señaló en un comentario sobre la Pregunta). De acuerdo con esa página de MSDN para la codificación de caracteres en .NET Framework :

El mapeo de ajuste óptimo es el comportamiento predeterminado para un objeto de codificación que codifica datos Unicode en datos de página de códigos ...

Pero, ¿qué son exactamente estas asignaciones? Esa página de MSDN solía decir lo siguiente:

Las estrategias de mejor ajuste varían para diferentes páginas de códigos, y no están documentadas en detalle.

Sin embargo, eso no era del todo correcto. Quizás las "estrategias" para determinar los mapeos no están exactamente documentadas. Okay. Pero, las asignaciones en sí están documentadas, pero no en los lugares más fáciles de encontrar.

Entonces, gracias a que Microsoft movió la documentación a GitHub, esa página ahora dice lo siguiente (porque lo actualicé):

Las estrategias de mejor ajuste no están documentadas en detalle. Sin embargo, varias páginas de códigos están documentadas en el sitio web del Consorcio Unicode . Revise el archivo readme.txt en esa carpeta para obtener una descripción de cómo interpretar los archivos de mapeo.

Si va a la siguiente URL, verá una lista de varios archivos, cada uno con el nombre de la página de códigos a la que asigna los caracteres Unicode:

ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WindowsBestFit/

La mayoría de los archivos se actualizaron por última vez (o al menos se colocaron allí) el 04/10/2006, y uno de ellos se actualizó el 14/03/2012. La primera parte de esos archivos asigna códigos ASCII en un punto de código Unicode equivalente. Pero la segunda parte de cada archivo asigna los caracteres Unicode a sus "equivalentes" ASCII.

Escribí un script de prueba que usa las asignaciones de la página de códigos 1252 para verificar si SQL Server realmente está usando esas asignaciones. Eso se puede determinar respondiendo estas dos preguntas:

  1. Para todos los puntos de código asignados, ¿SQL Server los convierte en las asignaciones especificadas?
  2. Para todos los puntos de código sin asignar, ¿SQL Server convierte alguno de ellos en un carácter que no sea " ?"?

El script de prueba es demasiado largo para colocarlo aquí, así que lo publiqué en Pastebin en:

Asignaciones de Unicode a la página de códigos en SQL Server

La ejecución del script mostrará que la respuesta a la primera pregunta anterior es "Sí" (lo que significa que se cumplen todas las asignaciones proporcionadas). También mostrará que la respuesta a la segunda pregunta es "No" (lo que significa que ninguno de los Puntos de código no asignados se convierte en otra cosa que no sea el carácter "desconocido"). Por lo tanto, ese archivo de mapeo es muy preciso :-).

Solomon Rutzky
fuente