为什么是SQL Server Big Endian?

Ata*_*rio 11 sql-server endianness

根据我的阅读,所有Windows版本和.NET都是小端.那么为什么偏离微软的SQL Server规范呢?

我的意思是"SQL Server是大端"是这样的:

SELECT CONVERT(VARBINARY, 255);
Run Code Online (Sandbox Code Playgroud)

得到:

0x000000FF
Run Code Online (Sandbox Code Playgroud)

并不是

0xFF000000
Run Code Online (Sandbox Code Playgroud)

类似于.NET的BitConverter.GetBytes()方式.我猜SQL Server可以在内部存储数字作为小端,然后CONVERT由于某种原因只是将其切换.但不管怎样,为什么?

编辑:

刚注意到这个......

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

给我:

01234567-89AB-CDEF-0123-456789ABCDEF

0x67452301AB89EFCD0123456789ABCDEF
Run Code Online (Sandbox Code Playgroud)

有没有搞错?

Sol*_*zky 15

是的:Windows和.NET是Little Endian.

那么为什么SQL Server Big Endian呢?容易:不是;-).排序规则和Unicode支持的MSDN页面(在SQL Server中)甚至指出:

由于英特尔平台是一种小端架构,因此Unicode代码字符始终以字节交换方式存储.

那么为什么在转换Int值为255时会得到Big Endian二进制值?这就是混乱的地方.这个问题是有缺陷的,因为它基于一个错误的前提:您应该看到转换后的值中反映的硬件和/或软件的字节序.但你为什么要这样?Endianness影响值的内部表示,如何存储.但它并没有改变事物本身.您可以将a转换DATETIME为an INT,您将看到一个Integer.但是如果将INTger保存在INT字段中,它将以相反的顺序存储为4个字节,因为这是Little Endian系统.但是,当您从系统请求该值并向您显示该值时,这与您看到的内容无关.

例如,运行以下命令以查看将INT值转换301BINARY(2)结果0x012D,因为0x012D = 301,仅为十六进制.正如预期的那样,转0x012D回到INT回报301.如果原始的Int到Binary转换给你0x2D01,那么,这不等于301.

SELECT CONVERT(BINARY(2), 301), CONVERT(INT, 0x012D)
-- 0x012D,  301
Run Code Online (Sandbox Code Playgroud)

但是,如果您创建一个包含INT列的表,并在该列中插入值"301",并用于DBCC PAGE查看磁盘上存在的数据页,您将按所示顺序看到以下十六进制数字:

2D 01 00 00
Run Code Online (Sandbox Code Playgroud)

另外,要解决一些支持问题前提的证据:

是的,BitConverter.ToString(BitConverter.GetBytes(255))在.NET中执行将返回:

FF-00-00-00

但是,这不是转换,因为GetBytes()它不转换"值",而是打算显示内部系统表示,这取决于系统是Little Endian还是Big Endian.如果您查看BitConverter.GetBytes的MSDN页面,它可能更清楚它实际上在做什么.

转换实际值时,不同系统的结果不会(也不会)不同.所有系统(甚至是计算器)的整数值256总是0x0100,因为Endianness与如何在基数10,基数2,基数16等之间转换值无关.

在.NET中,如果要进行此转换,可以使用String.Format("{0:X8}", 255)哪个将返回:

000000FF

这与SELECT CONVERT(BINARY(4), 255);返回的内容相同,因为它们都转换了值.这个结果没有显示为Big Endian,而是显示为它真正的,恰好匹配Big Endian的字节顺序.

换句话说,当以比特序列开始时100000000,可以用十进制形式表示256,或者以十六进制形式(称为BINARY/ VARBINARY在SQL Server中)表示为0x0100.Endianness与此无关,因为这些仅仅是表示相同基础值的不同方式.

VARBINARY和之间转换时,可以看到SQL Server是Little Endian的进一步证据NVARCHAR.由于NVARCHAR是16位(即2字节)编码,我们可以看到字节排序,因为字符没有数字等价物(不像256 - > 0x0100示例),因此实际上没有其他东西可以显示(显示代码点值)因补充字符而不是一个选项.

如下所示,拉丁语大写A的代码点为U + 0041(在数字上与65相同)转换为VARBINARY0x4100,因为这是该字符的UTF-16 Little Endian编码值:

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
Run Code Online (Sandbox Code Playgroud)

此外,使用代理对"D83D + DCA9"(函数允许)可以看到" Poo堆 "表情符号(代码点U + 01F4A9 NCHAR),或者您可以注入UTF-16 Little Endian字节序列:

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

UNIQUEIDENTIFIER类似的是"它是什么"和"如何存储"是两个不同的东西,它们不需要匹配.请记住,UUID/GUID不是像int或的基本数据类型char,但更像是具有已定义格式的实体,就像JPG或MP3文件一样.UNIQUEIDENTIFIER在我对DBA.StackExcange 相关问题的回答中有更多关于s的讨论(包括为什么它由Big Endian和Little Endian组合表示).