为什么是SQL Server Big Endian?

时间:2014-02-15 04:19:42

标签: sql-server endianness

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

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

SELECT CONVERT(VARBINARY, 255);

给出:

0x000000FF

而不是

0xFF000000

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

编辑:

刚刚注意到这一点......

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

给了我:

01234567-89AB-CDEF-0123-456789ABCDEF

0x67452301AB89EFCD0123456789ABCDEF

到底是什么?

1 个答案:

答案 0 :(得分:15)

是的:Windows和.NET是Little Endian。

那么为什么SQL Server Big Endian?容易:不是;-)。 Collation and Unicode Support(在SQL Server中)的MSDN页面甚至声明:

  

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

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

例如,运行以下内容以查看将INT的{​​{1}}值转换为301会导致BINARY(2),因为0x012D = 301,只有十六进制。因此,按预期方式将0x012D转换回0x012D会返回INT。如果原始的Int到Binary转换给你0x2D01,那么,这不等于301。

301

但是,如果您创建一个包含SELECT CONVERT(BINARY(2), 301), CONVERT(INT, 0x012D) -- 0x012D, 301 列的表,并在该列中插入值“301”,并使用INT查看磁盘上存在的数据页,将按所示顺序看到以下十六进制数字:

DBCC PAGE

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

是的,在.NET中执行2D 01 00 00 将返回:

  

FF-00-00-00

但是,不是转换,因为BitConverter.ToString(BitConverter.GetBytes(255))没有转换“值”,而是打算显示内部系统表示,这取决于系统是否是小的Endian或Big Endian。如果您查看BitConverter.GetBytes的MSDN页面,可能会更清楚它实际上在做什么。

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

在.NET中,如果要进行此转换,可以使用将返回的GetBytes()

  

000000FF

String.Format("{0:X8}", 255)返回的内容相同,因为它们都转换了值。这个结果没有显示为Big Endian,而是显示为真正的结果,恰好匹配Big Endian的字节顺序。

换句话说,当从SELECT CONVERT(BINARY(4), 255);的位序列开始时,可​​以用十进制形式表示为100000000,或者以十六进制形式(称为256 / {{1 } {在} SQL Server内)BINARY。 Endianness与此无关,因为它们只是表示相同基础值的不同方式。

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

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

A

此外,使用代理对“D83D + DCA9”(VARBINARY功能允许)可以看到“Pile of Poo”表情符号(代码点U + 01F4A9),或者您可以注入UTF-16 Little Endian字节序列:

0x4100

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 类似于“它是什么”和“如何存储”是两个不同的东西,它们不需要匹配。请记住,UUID / GUID不是像NCHARSELECT NCHAR(0xD83D) + NCHAR(0xDCA9) AS [SurrogatePair], CONVERT(NVARCHAR(5), 0x3DD8A9DC) AS [UTF-16LE]; -- 这样的基本数据类型,而是更像是具有已定义格式的实体,就像JPG或MP3文件一样。在我对DBA.StackExcange上的related question的回答中有更多关于UNIQUEIDENTIFIER的讨论(包括为什么它由Big Endian和Little Endian组合表示)。