AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 326560
Accepted
Denis Rubashkin
Denis Rubashkin
Asked: 2023-04-28 23:45:41 +0800 CST2023-04-28 23:45:41 +0800 CST 2023-04-28 23:45:41 +0800 CST

哈萨克语不区分大小写排序规则中的字符问题

  • 772

我对西里尔字符“E”和“e”有疑问,当使用不区分大小写的排序规则时,它们应该相等。这对于除哈萨克语之外的所有归类都是正确的。

我使用以下查询检查了所有 CI 排序规则:

SELECT 'DECLARE @Test_' + name + ' TABLE (
    Code nvarchar(32) COLLATE ' + name + '
)

INSERT @Test_' + name + ' (Code)
VALUES (N''Е''), (N''е'')

SELECT ''' + name + ''', * FROM @Test_' + name + ' WHERE Code = N''Е'' COLLATE ' + name 

FROM sys.fn_helpcollations()
WHERE name LIKE '%CI%'

对于所有排序规则,但不是哈萨克语,都会按预期返回两行。

用于说明问题的示例查询:

SET NOCOUNT ON;

DECLARE @Test TABLE (
    Code nvarchar(32) COLLATE Kazakh_90_CI_AS
);

DECLARE @UpperChar  nchar(1) = N'Е';
DECLARE @LowerChar  nchar(1) = N'е';

SELECT  ASCII(@UpperChar) AS 'UpperChar ASCII', ASCII(@LowerChar) AS 'LowerChar ASCII';

/* Just ASCII-codes for the chars

UpperChar ASCII LowerChar ASCII
--------------- ---------------
197             229
*/

INSERT @Test (Code)
VALUES (@UpperChar), (@LowerChar);

SELECT DISTINCT Code AS 'DISTINCT Code' FROM @Test;

/* Should be one row with CI collation - FALSE

DISTINCT Code
--------------------------------
Е
е
*/

SELECT Code AS 'Code = @UpperChar'
FROM @Test
WHERE Code = @UpperChar;

/* Should be two rows with CI collation - FALSE

Code = @UpperChar
--------------------------------
Е
*/

SELECT Code AS 'Code = @LowerChar'
FROM @Test 
WHERE Code = @LowerChar;

/* Should be two rows with CI collation - FALSE

Code = @LowerChar
--------------------------------
е
*/

SELECT Code AS 'Code = @UpperChar OR Code = LOWER(@UpperChar)'
FROM @Test
WHERE Code = @UpperChar
    OR Code = LOWER(@UpperChar);

/*Check LOWER('Е') = 'е'  - TRUE

Code = @UpperChar OR Code = LOWER(@UpperChar)
---------------------------------------------
Е
е
*/

SELECT Code AS 'Code = @LowerChar OR Code = UPPER(@LowerChar)'
FROM @Test 
WHERE Code = @LowerChar
    OR Code = UPPER(@LowerChar);

/*Check UPPER('е') = 'Е' - TRUE

Code = @LowerChar OR Code = UPPER(@LowerChar)
---------------------------------------------
Е
е
*/

其他 Сyrillic 字符的行为与预期一致。

我可以做什么来解决这个问题?

sql-server
  • 1 1 个回答
  • 147 Views

1 个回答

  • Voted
  1. Best Answer
    Solomon Rutzky
    2023-05-05T23:23:33+08:002023-05-05T23:23:33+08:00

    在我们进入细节之前,这里有两件事通常会有所帮助:

    • 该ASCII()函数用于VARCHAR数据并且对与数据排序规则关联的代码页敏感(对于变量,它是当前数据库的默认排序规则)。但在这种情况下,我们只处理 Unicode /NVARCHAR数据,因此UNICODE()应该改用函数。
    • CHAR()当处理 ASCII 值/代码点大于 127 的字符时,让脚本使用或函数创建特定字符会很有帮助NCHAR()。这使得脚本更易于传输,因为在将脚本打开/粘贴到不支持某些字符的环境中时不会出现字符转换问题。而且,它使脚本更具可读性/可理解性,因为在处理看起来像其他角色但实际上不同的角色(例如我们在这里处理的角色)时,读者不会感到困惑。

    现在,先介绍一些背景知识,这样解释才有意义:

    Unicode 排序/比较是通过为每个字符分配多个权重来完成的。两个权重类别是大小写和变音符号(即重音)。拥有多个类别可以更轻松地处理区分大小写与不区分大小写以及区分重音与不区分大小写的各种组合。大多数(如果不是全部)定义的字符都有默认的排序权重。当使用特定的文化/区域设置时,这些默认权重可以被特定于文化的值覆盖。使用美国英语时,将使用默认值(即无覆盖)。这就是为什么其他语言的字符即使在使用归类时仍然经常正确排序(或大部分正确) ,以及为什么美国英语在使用(或等)归类Latin1_General时仍能正确工作(因为HebrewJapaneseHebrew排序规则不会覆盖美国英语字符的权重)。

    每个字符的各种权重都保存在一个文件中。大写和小写映射位于单独的文件中。而且,虽然 Unicode Consortium 已进入每年更新的节奏,但 Microsoft 的更新频率要低得多。根据他们公开可用的排序权重文件,他们只有以下版本(适用于 Windows):

    1. Windows NT 4.0 到 Windows Server 2003
    2. 视窗Vista
    3. 视窗服务器 2008
    4. Windows 7 和 Windows 服务器 2008 R2
    5. Windows 8 和 Windows 服务器 2012
    6. 视窗 10

    SQL Server 的版本较少:

    1. 名称中没有版本号的排序规则是版本“80”(即在 SQL Server 2000 中引入)
    2. _90_在 SQL Server 2005 中引入了名称中带有的排序​​规则
    3. _100_SQL Server 2008 中引入了名称中带有的排序​​规则
    4. _140_在 SQL Server 2017 中引入了名称中带有的排序​​规则(遗憾的是,此版本的唯一排序规则是日文排序规则)

    最后,请记住 SQL Server 排序规则基于Windows 排序规则,但并不完全相同。我认为版本 100 排序规则与“Windows Server 2008”文件关联,而版本 80 和 90 排序规则应该与“Windows NT 4.0 到 Windows Server 2003”文件关联最密切(自 Vista 于 2007 年发布以来) .

    考虑到所有这些:

    只是为了让任何阅读本文的人都不会感到困惑:这里有问题的字符是“西里尔大写字母 Ie:Е”和“西里尔小写字母 Ie:е”(分别是 Unicode 代码点 U+0415 和 U+0435),看起来与拉丁字符“E”和“e”相同,但绝对不同。例如:

    SELECT  NCHAR(0x0415) AS [Cyrillic Capital Letter Ie],
            NCHAR(0x0045) AS [Latin Capital Letter E],
            IIF(NCHAR(0x0415) = NCHAR(0x0045) COLLATE Kazakh_100_CI_AI, '=', '<>') AS [Kazakh],
            IIF(NCHAR(0x0415) = NCHAR(0x0045) COLLATE Latin1_General_100_CI_AI, '=', '<>')
              AS [Latin1_General]
    

    回报:

    Cyrillic Capital Letter Ie    Latin Capital Letter E    Kazakh    Latin1_General
    Е                             E                         <>        <>
    

    所有 Microsoft 排序权重文件的默认行为是这两个字符除了大小写外都是相同的。这就是为什么它们在除排序规则之外的所有不区分大小写的排序规则中比较相等的原因Kazakh。

    为什么这两个字符——U+0415 和 U+0435——在使用不区分大小写的哈萨克语归类时比较不同?因为(无论出于何种原因),在使用哈萨克语时,“Windows NT 4.0 到 Windows Server 2003”和“Windows Server 2008”排序权重文件都包含对 U+0435(“西里尔小写字母 Ie:е”)的覆盖文化。覆盖使此字符 U+0435 等同于以下字符(以及其他一些字符):

    -- case-sensitive
    SELECT  NCHAR(0x0435) AS [Cyrillic Small Letter Ie], -- е
            NCHAR(0x04bf) AS [Cyrillic Small Letter Abkhasian Che With Descender], -- ҿ
            IIF(NCHAR(0x0435) = NCHAR(0x04bf) COLLATE Kazakh_90_CS_AI, '=', '<>') AS [Kazakh_90], -- =
            IIF(NCHAR(0x0435) = NCHAR(0x04bf) COLLATE Kazakh_100_CS_AI, '=', '<>') AS [Kazakh_100], -- =
            IIF(NCHAR(0x0435) = NCHAR(0x04bf) COLLATE Latin1_General_CS_AI, '=', '<>') AS [Latin1_General], -- =
            IIF(NCHAR(0x0435) = NCHAR(0x04bf) COLLATE Latin1_General_100_CS_AI, '=', '<>') AS [Latin1_General_100] -- <>
    
    -- accent-sensitive
    SELECT  NCHAR(0x0435) AS [Cyrillic Small Letter Ie], -- е
            NCHAR(0x0404) AS [Cyrillic Capital Letter E], -- Є
            IIF(NCHAR(0x0435) = NCHAR(0x0404) COLLATE Kazakh_90_CI_AS, '=', '<>') AS [Kazakh_90],-- <>
            IIF(NCHAR(0x0435) = NCHAR(0x0404) COLLATE Kazakh_100_CI_AS, '=', '<>') AS [Kazakh_100], -- =
            IIF(NCHAR(0x0435) = NCHAR(0x0404) COLLATE Latin1_General_CI_AS, '=', '<>') AS [Latin1_General],-- <>
            IIF(NCHAR(0x0435) = NCHAR(0x0404) COLLATE Latin1_General_100_CI_AS, '=', '<>') AS [Latin1_General_100]-- <>
    
    -- case and accent -sensitive
    SELECT  NCHAR(0x0435) AS [Cyrillic Small Letter Ie], -- е
            NCHAR(0x0454) AS [Cyrillic Small Letter E], -- є
            IIF(NCHAR(0x0435) = NCHAR(0x0454) COLLATE Kazakh_90_CS_AS, '=', '<>') AS [Kazakh_90], -- <>
            IIF(NCHAR(0x0435) = NCHAR(0x0454) COLLATE Kazakh_100_CS_AS, '=', '<>') AS [Kazakh_100], -- =
            IIF(NCHAR(0x0435) = NCHAR(0x0454) COLLATE Latin1_General_CS_AS, '=', '<>') AS [Latin1_General], -- <>
            IIF(NCHAR(0x0435) = NCHAR(0x0454) COLLATE Latin1_General_100_CS_AS, '=', '<>') AS [Latin1_General_100] -- <>
    

    哈萨克语特定的覆盖还导致字符“西里尔小写字母 Io”(U+0451) 不再等同于其大写字符:

    -- Also messed up, just like Ie; accent-sensitive
    SELECT  NCHAR(0x0451) AS [Cyrillic Small Letter Io], -- ё
            NCHAR(0x0401) AS [Cyrillic Capital Letter Io], -- Ё
            IIF(NCHAR(0x0451) = NCHAR(0x0401) COLLATE Kazakh_90_CI_AS, '=', '<>') AS [Kazakh_90],-- <>
            IIF(NCHAR(0x0451) = NCHAR(0x0401) COLLATE Kazakh_100_CI_AS, '=', '<>') AS [Kazakh_100], -- <>
            IIF(NCHAR(0x0451) = NCHAR(0x0401) COLLATE Latin1_General_CI_AS, '=', '<>') AS [Latin1_General],-- =
            IIF(NCHAR(0x0451) = NCHAR(0x0401) COLLATE Latin1_General_100_CI_AS, '=', '<>') AS [Latin1_General_100]-- =
    

    所有这些行为都是 Microsoft 最初实施 Unicode 的残余(回到 Windows NT 4.0!)。值得赞扬的是,Microsoft 是 Unicode 的早期采用者,这种行为可能就像它在 Unicode 1.0 版中的定义一样。很难确定,因为要找到 Unicode 的原始整理文件并不容易(我认为 2.1 版是我能找到的最早版本)。但是,我可以说,通过查看 Microsoft 提供的文件,即使他们更新了默认排序权重并在每个文件中添加了字符和文化,一些定义(例如哈萨克语特定的覆盖)直到最近才更新. 事实上,直到“Windows 8 和 Windows Server 2012”文件(即第二组最近的定义),他们才修复哈萨克语特定的覆盖(我假设其他)。

    因此,即使 Windows 通常(也可能还有 .NET)正确处理哈萨克语排序规则(从 Windows 8 和 Windows Server 2012 开始),SQL Server 排序规则仍然停留在过去。意思是,微软将这些归类更新为更新版本的 Unicode 是一个奇迹,没有解决办法。我确实有一个想法,我一直在考虑向他们提议如何使它比简单的定义更新更好,所以也许我应该继续提交(我会在完成后用链接更新这个答案那)。

    但是现在,如果你真的需要这两个字符在不区分大小写的哈萨克语排序规则中相等(并且不要忘记“西里尔小写字母 Io”(U+0451)),你将不得不从你的例子中做到这UPPER()一点(虽然我不能肯定地说这种解决方法不会引起任何问题)。


    有关排序规则、Unicode 和编码的更多信息,请访问我的网站:Collat​​ions.Info

    • 3

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve