据我所知,所有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
到底是什么?
答案 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与此无关,因为它们只是表示相同基础值的不同方式。
在VARBINARY
和0x0100
之间进行转换时,可以看到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不是像NCHAR
或SELECT NCHAR(0xD83D) + NCHAR(0xDCA9) AS [SurrogatePair],
CONVERT(NVARCHAR(5), 0x3DD8A9DC) AS [UTF-16LE];
--
这样的基本数据类型,而是更像是具有已定义格式的实体,就像JPG或MP3文件一样。在我对DBA.StackExcange上的related question的回答中有更多关于UNIQUEIDENTIFIER
的讨论(包括为什么它由Big Endian和Little Endian组合表示)。