【问题标题】:Weird Comparison Result in SQL ServerSQL Server 中奇怪的比较结果
【发布时间】:2017-08-22 03:39:21
【问题描述】:

对于没有依赖于排序规则的明确顺序的字符(即,不折叠大小写和/或没有去除重音符号),排序基于代码点的序号值基本字符集。如果一个字符串是另一个字符串的前缀,则较短的字符串首先排序。

Unicode 中的前 128 个代码点与 ASCII 字符集相同,这意味着 / 在 - 之后。 ---- 来自 cco 的回答。

那为什么会出现以下情况呢?

我希望 '2017/8/22 1:33:53' 出现在 '2017-08-13 23:12:33.411' 之后,因为 / 出现在 - 之后。这是因为 Chinese_PRC_CI_AS 排序规则中的特殊排序吗?如果是这种情况,我在哪里可以找到规范?

【问题讨论】:

  • 您要做的最后一件事是比较包含在字符串中的日期时间。在比较之前将这些字符串转换或转换为 DATETIME 类型。更好的是,不要将日期时间存储为字符串。
  • @TT。感谢您的提醒!如果数据库是由我创建的,我当然不会这样做......但无论如何,关于这个问题的任何想法(特别是为什么会发生这种奇怪的比较结果)?谢谢!
  • 长度肯定被考虑在内,这不依赖于排序规则。执行以下命令:DECLARE @t TABLE (v NVARCHAR(3)); INSERT INTO @t(v)VALUES('1'),('01'),('10'),('2'),('3'),('300'),('301'); SELECT*FROM @t ORDER BY v;。您会看到 102 之前出现。如果字符串的长度可以变化,或者如果使用其他字符作为分隔符,则无法比较字符串中的日期时间。
  • 请查看此问题,了解为什么字符串 '2017/8/22 1:33:53' 在 '2017-08-13 23:12:33.411' 之前。没关系,它实际上是日期时间。当我们比较字符串时,前者应该在后者之后,而它不在上图中。

标签: sql-server sql-server-2008 collation sql-server-2016 codepages


【解决方案1】:

我预计 '2017/8/22 1:33:53' 将在 '2017-08-13 23:12:33.411' 之后出现 因为 / 在 - 之后。这是因为特殊订购 Chinese_PRC_CI_AS 排序规则?

是的,这是因为 Chinese_PRC_CI_AS 排序规则

如果是这种情况,我在哪里可以找到规范?

规则很复杂,但您可以在这里找到它们: UNICODE COLLATION ALGORITHM

我从中提取一些文字作为图片给你一个想法:

在您的情况下,您可以尝试使用binary 排序规则Chinese_PRC_BIN2,它会在这种特殊情况下为您提供理想的结果,但它对于字母排序可能是不可接受的,我的意思是在欧洲语言中二进制排序将总是把大写字母放在所有小写字母之前,但我不知道应该如何对中文符号进行排序

这里是根据订单对日期进行排序的代码(你说你使用 UNICODE 列):

declare @t table (s nvarchar(100))
insert into @t values (N'2017/8/22 1:33:53'),  (N'2017-08-13 23:12:33.411')

select *
from @t
order by s collate Chinese_PRC_BIN2;

Bin2 排序规则也可以在“非 unicode 大小写”下工作,就像您在图片中的示例一样:

select case 
      when '2017/8/22 1:33:53' collate Chinese_PRC_BIN2 < 
           '2017-08-13 23:12:33.411' collate Chinese_PRC_BIN2 
      then 'TRUE' 
      else 'FALSE' 
   end;

【讨论】:

    【解决方案2】:

    不仅仅是 ASCII 码。

    是的,/ 的 ASCII 码是 0x2F,- 的 ASCII 码是 0x2D,但是字符串比较规则取决于排序规则,这些规则可能非常复杂,不仅要考虑字符代码的值。

    - 符号在某些排序规则中以特殊方式处理。

    这是一个完整的例子:

    DECLARE @T1 TABLE (Value varchar(100) COLLATE Chinese_PRC_CI_AS);
    DECLARE @T2 TABLE (Value varchar(100) COLLATE Latin1_General_CI_AS);
    DECLARE @T3 TABLE (Value varchar(100) COLLATE SQL_Latin1_General_CP1_CI_AS);
    
    INSERT INTO @T1 VALUES
    ('abc'),
    ('abc-def'),
    ('abcdef'),
    ('abc-');
    
    INSERT INTO @T2 VALUES
    ('abc'),
    ('abc-def'),
    ('abcdef'),
    ('abc-');
    
    INSERT INTO @T3 VALUES
    ('abc'),
    ('abc-def'),
    ('abcdef'),
    ('abc-');
    
    SELECT * FROM @T1 ORDER BY Value;
    SELECT * FROM @T2 ORDER BY Value;
    SELECT * FROM @T3 ORDER BY Value;
    

    结果

    T1 (Chinese_PRC_CI_AS)

    +---------+
    |  Value  |
    +---------+
    | abc     |
    | abc-    |
    | abcdef  |
    | abc-def |
    +---------+
    

    T2 (Latin1_General_CI_AS)

    +---------+
    |  Value  |
    +---------+
    | abc     |
    | abc-    |
    | abcdef  |
    | abc-def |
    +---------+
    

    T3 (SQL_Latin1_General_CP1_CI_AS)

    +---------+
    |  Value  |
    +---------+
    | abc     |
    | abc-    |
    | abc-def |
    | abcdef  |
    +---------+
    

    请注意,第三个表中的 SQL 排序规则产生的结果顺序不同。


    另请注意,如果您将列类型更改为nvarchar,则此特定示例中的效果会消失。换句话说,处理- 符号的规则取决于排序规则和值的类型。

    【讨论】:

    • 在我的例子中,列和数据类型是 nvarchar。在将字符串显式转换为 nvarchar 后,“问题”(对我来说是一种问题,但从 MS 的角度来看可能是设计使然)仍然存在。
    • 也可能是因为您提到的“复杂规则”?我在哪里可以找到这些规则?
    • 对不起,瑞恩。我不知道在哪里可以找到排序规则的详细说明。试着问另一个具体的问题。也许有人会回答。
    • 感谢@Vladimir,您的回答仍然非常有用!
    【解决方案3】:

    对于没有依赖于排序规则的明确顺序的字符(即,不折叠大小写和/或没有去除重音符号),排序基于代码点的序号值基本字符集。如果一个字符串是另一个字符串的前缀,则较短的字符串首先排序。
    Unicode 中的前 128 个代码点与 ASCII 字符集相同,这意味着 /- 之后。

    【讨论】:

    • 那为什么 8 月 22 日早于 8 月 13 日(在前面的示例中),正如在 sql server 2016 和 2008 上测试的那样?
    • 我编辑了我的问题以便更清楚。请查看更新版本。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多