繁体   English   中英

SQL Server varchar与nvarchar的意外“排序依据”差异

[英]SQL Server unexpected 'order by' difference for varchar vs. nvarchar

我在varchar与nvarchar数据的“ order by”结果之间遇到了意外的差异。 在这两种情况下,所讨论的数据均来自旧的ASCII字符集。 区别在于以nnn与-nnn开头的数据排序,其中n是一个数字。

下面是重现该问题的SQL Server脚本; 我的测试服务器是SQL 2016,但我在2008年和2012年也重现了该问题。 我尝试了不同的排序规则,但没有任何效果(Latin1_General_bin除外,请参见下文)。 该脚本会创建2个示例表,与我们的应用程序中的一个相似,一个使用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'; 我已将数据编辑到最小程度以证明问题所在。

右填充空白时,所有条目的长度均相同,无效。

使用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行)

_FldNum _ValidationEntry

1           0  
1           1  
1           -1  
1           10  
1           -10  
1           100  
1           -100  

(受影响的7行)

当SQL Server解析ORDER BY ,它不仅根据排序规则来确定行的顺序,还可以根据数据类型来确定行的顺序。

最后,varchar和nchar都是二进制文件。 问题是,在varchar中,“-”符号在数字/字符之后(当涉及二进制表示法时),对于nvarchar,情况恰恰相反。 在Internet上查看ASCI / UNICODE表。

因此,由于ORDER BY是比较二进制文件,因此nvarchar的负值首先出现。

如果您对数据的实际存储方式更感兴趣,那么“ Microsoft SQL Server内部”一书可能对您很有趣。 有整节讨论此问题。

编辑:

要查看数据实际上是如何存储在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排序规则时,column按其二进制表示形式排序,因此, 2D负号在前。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM