我在'排序'的结果之间遇到了意想不到的差异。使用varchar与nvarchar数据。在这两种情况下,所讨论的数据都来自旧的ASCII字符集;在以nnn vs -nnn开头的数据排序中出现差异,其中n是数字。
下面是重现问题的SQL Server脚本;我的测试服务器是SQL 2016,但我也在2008年和2012年重现了这个问题。我尝试了不同的排序规则而没有效果(Latin1_General_bin除外,见下文)。该脚本创建了两个类似于我们的应用程序中的样本表,一个使用varchar,另一个使用nvarchar,并添加了7行数据。
CREATE TABLE [dbo].[_ValidationLists](
[_FldNum] [int] NOT NULL,
[_ValidationEntry] [varchar](250) NOT NULL,
CONSTRAINT [PK__ValidationLists] PRIMARY KEY CLUSTERED
(
[_ValidationEntry] ASC,
[_FldNum] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[_ValidationListsN](
[_FldNum] [int] NOT NULL,
[_ValidationEntry] [nvarchar](250) NOT NULL,
CONSTRAINT [PK__ValidationListsN] PRIMARY KEY CLUSTERED
(
[_ValidationEntry] ASC,
[_FldNum] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
INSERT INTO [_ValidationLists] (_fldnum, _ValidationEntry) VALUES (1,'-1'), (1,'-10'), (1,'-100'), (1,'0'), (1,'1'), (1,'10'), (1,'100')
INSERT INTO [_ValidationListsN] (_fldnum, _ValidationEntry) VALUES (1,N'-1'), (1,N'-10'), (1,N'-100'), (1,N'0'), (1,N'1'), (1,N'10'), (1,N'100')
select * from [_ValidationLists]
order by [_ValidationEntry] asc
select * from [_ValidationListsN]
order by [_ValidationEntry] asc
select语句的结果如下。 varchar的第一个结果是我所期望的(词典排序);第二个结果我无法解释。第一个是我们的客户群所期望的,我们被这个结果感到惊讶。 (客户数据不常见 - 通常此表用于alpha数据;而且对于varchar和nvarchar,alpha数据顺序相同。)
结果是相同的使用N' ...'初始化_ValidationListsN行。原始数据的条目较长,例如' -100:Pass&#39 ;;我已将数据编辑到最少,这证明了问题。
使用空白进行右边填充,因此所有条目长度相同都没有效果。
使用COLLATE Latin1_General_bin重现词典排序,但是不可接受,因为(仅有一个原因)我们通常使用不区分大小写的排序。
我们报告此问题的客户只有ASCII数据,因此我们可以通过使用varchar重新创建此表来修复它们。我很想知道为什么nvarchar会以这种方式运行,因为结果对我来说似乎不正确,并且如果有办法获得我们期望的排序行为(第一种情况)。至少我不知道为什么所有条目都以' - '开头。 (ASCII 0X2d,短划线或减号)不要一起订购。
_FldNum _ValidationEntry
1 -1
1 -10
1 -100
1 0
1 1
1 10
1 100
(7行(s)受影响)
_FldNum _ValidationEntry
1 0
1 1
1 -1
1 10
1 -10
1 100
1 -100
(7行(s)受影响)
答案 0 :(得分:0)
当SQL Server解析ORDER BY
时,它不仅从排序规则决定行的顺序,而且如您已经发现的那样,从数据类型决定行的顺序。
varchar和nchar最后只是二进制文件。问题是,在varchar中,“ - ”符号出现在数字/字符之后(当涉及二进制表示法时),对于nvarchar,它是相反的。检查互联网上的ASCI / UNICODE表。
正因为如此,由于ORDER BY
最终会比较二进制,所以你的负值首先是nvarchar。
如果您对如何实际存储数据更感兴趣,请预订“Microsoft SQL Server Internals”可能对您有意义。有一整节讨论这个问题。
编辑:
要了解数据如何实际存储在db中,请参阅以下代码段:
SELECT
[_ValidationEntry],
CONVERT(binary(6), [_ValidationEntry]) AS [BinaryRepresentation]
FROM [_ValidationLists]
--ORDER BY [_ValidationEntry] COLLATE Latin1_General_bin
SELECT
[_ValidationEntry],
CONVERT(binary(6), [_ValidationEntry]) AS [BinaryRepresentation]
FROM [_ValidationListsN]
--ORDER BY [_ValidationEntry] COLLATE Latin1_General_bin
查询结果如下:
0 0x300000000000
1 0x310000000000
-1 0x2D3100000000
10 0x313000000000
-10 0x2D3130000000
100 0x313030000000
-100 0x2D3130300000
使用BIN归类时,列按其二进制表示排序,因此,2D
减号首先出现。