Pregunta ¿Por qué es SQL Server Big Endian?


Por lo que he leído, todas las versiones de Windows y .NET son poco endian. Entonces, ¿por qué la salida de la norma de Microsoft para SQL Server?

Lo que quiero decir con "SQL Server is big endian" es esto:

SELECT CONVERT(VARBINARY, 255);

da:

0x000000FF

y no

0xFF000000

la forma en que algo como .NET BitConverter.GetBytes() hace. Supongo que SQL Server podría almacenar el número internamente como little endian y luego CONVERT simplemente lo está cambiando por alguna razón. Pero de cualquier manera, ¿por qué?

Editar:

Solo noté esto ...

DECLARE @q UNIQUEIDENTIFIER = '01234567-89ab-cdef-0123-456789abcdef';
SELECT @q;
SELECT CONVERT(VARBINARY, @q);

me dio:

01234567-89AB-CDEF-0123-456789ABCDEF

0x67452301AB89EFCD0123456789ABCDEF

¿Qué diablos?


9
2018-02-15 04:19


origen


Respuestas:


Sí: Windows y .NET son Little Endian.

Entonces, ¿por qué es SQL Server Big Endian? Fácil: no lo es ;-). La página de MSDN para Colación y soporte Unicode (dentro de SQL Server) incluso dice:

Debido a que la plataforma Intel es una arquitectura little endian, los caracteres de código Unicode siempre se almacenan mediante intercambio de bytes.

Entonces, ¿por qué obtienes un valor binario de Big Endian al convertir el valor Int de 255? Aquí es donde está la confusión. Esta pregunta es errónea porque se basa en una premisa falsa: que debe ver la endianidad del hardware y / o software reflejados en el valor convertido. ¿Pero por qué lo harías? Endianness afecta la representación interna de un valor, cómo se almacena. Pero no cambia la cosa en sí. Puedes convertir un DATETIME a una INT y verá un Entero. Pero si guarda ese entero en un campo INT, se almacenará como 4 bytes en orden inverso, ya que este es un sistema Little Endian. Pero eso no tiene nada que ver con lo que ve cuando solicita ese valor del sistema y se lo muestra a usted.

Por ejemplo, ejecute lo siguiente para ver que la conversión de INT valor de 301 a un BINARY(2) resultados en 0x012D, porque 0x012D = 301, solo en hexadecimal. Y así convertir 0x012D de regreso INT devoluciones 301, como se esperaba. Si la conversión original de Int a Binario le dio 0x2D01, bueno, eso no equivale a 301.

SELECT CONVERT(BINARY(2), 301), CONVERT(INT, 0x012D)
-- 0x012D,  301

SIN EMBARGO, si crea una tabla con un INT columna, e inserte un valor de "301" en esa columna, y use DBCC PAGE para ver la página de datos tal como existe en el disco, verá los siguientes dígitos hexadecimales en el orden que se muestra:

2D 01 00 00

Además, para abordar algunas de las pruebas que respaldan la premisa de la pregunta:

Sí, haciendo BitConverter.ToString(BitConverter.GetBytes(255)) en .NET devolverá:

FF-00-00-00

Pero eso es no una conversión como GetBytes() no está convirtiendo el "valor", sino que intenta mostrar la representación interna del sistema, que cambia según si el sistema es Little Endian o Big Endian. Si miras la página de MSDN para BitConverter.GetBytes, podría ser más claro en cuanto a lo que realmente está haciendo.

Al convertir valores reales, los resultados no serán (y no pueden) diferentes en diferentes sistemas. Un valor entero de 256 siempre será 0x0100 en todos los sistemas (incluso calculadoras) porque Endianness no tiene nada que ver con cómo se convierten los valores entre la base 10, la base 2, la base 16, etc.

En .NET, si quieres hacer esta conversión, puedes usar String.Format("{0:X8}", 255) que regresará:

000000FF

que es lo mismo que SELECT CONVERT(BINARY(4), 255); regresa ya que ambos están convirtiendo el valor. Este resultado no se muestra como Big Endian, pero se muestra como realmente es, que coincide con el orden de bytes de Big Endian.

En otras palabras, cuando se comienza con una secuencia de bits de 100000000, que se puede representar en forma decimal como 256, o en forma hexadecimal (conocido como BINARY / VARBINARY dentro de SQL Server) como 0x0100. Endianness no tiene nada que ver con esto, ya que estas son simplemente formas diferentes de representar el mismo valor subyacente.

Se puede ver más evidencia de que SQL Server es Little Endian cuando se convierte entre VARBINARY y NVARCHAR. Ya que NVARCHAR es una codificación de 16 bits (es decir, 2 bytes), podemos ver el orden de bytes ya que no hay un equivalente numérico para los caracteres (a diferencia del ejemplo 256 -> 0x0100) y realmente no hay nada más que mostrar (mostrar los valores del punto de código es no es una opción debido a caracteres suplementarios).

Como puede ver a continuación, un capital latino A, que tiene un punto de código de U + 0041 (que es numéricamente el mismo que 65) se convierte en un VARBINARY valor de 0x4100, porque ese es el valor codificado UTF-16 Little Endian de ese personaje:

SELECT CONVERT(VARBINARY(10), N'A'), -- 0x4100
       CONVERT(NVARCHAR(5), 0x4100), -- A
       CONVERT(INT, 0x4100),         -- 16640
       UNICODE(N'A'),                -- 65
       CONVERT(VARBINARY(8), 65);    -- 0x00000041

SELECT CONVERT(VARBINARY(10), N'ᄀ'), -- 0x0011
       CONVERT(NVARCHAR(5), 0x0011),  -- ᄀ
       CONVERT(INT, 0x0011),          -- 17
       UNICODE(N'ᄀ'),                -- 4352
       CONVERT(VARBINARY(8), 4352);   -- 0x00001100

También el "Pila de Poo"emoji (punto de código U + 01F4A9) se puede ver utilizando el par suplente" D83D + DCA9 "(que el NCHAR función permite), o puede inyectar la secuencia de bytes Little Endian UTF-16:

SELECT NCHAR(0xD83D) + NCHAR(0xDCA9) AS [SurrogatePair],
       CONVERT(NVARCHAR(5), 0x3DD8A9DC) AS [UTF-16LE];
--    

UNIQUEIDENTIFIER es similar en que "lo que es" y "cómo se almacena" son dos cosas diferentes y no es necesario que coincidan. Tenga en cuenta que UUID / GUID no es un tipo de datos básico como int o char, pero es más una entidad que tiene un formato definido, al igual que los archivos JPG o MP3. Hay más discusión sobre UNIQUEIDENTIFIERs en mi respuesta a un pregunta relacionada en DBA.StackExcange (incluyendo por qué está representado por una combinación de Big Endian y Little Endian).


13
2017-11-24 06:07