【问题标题】:SQL sort order in Japanese breaks when text includes non-Japanese characters当文本包含非日语字符时,日语中的 SQL 排序顺序会中断
【发布时间】:2019-12-09 13:39:10
【问题描述】:

当文本包含非日语文本时,日语排序似乎“中断”,即使在查询的 sort 部分之后强制进行任何可能的排序时也是如此。

我想知道这是否是一种已知现象,以及可能的解决方案。

最后我在寻找不区分假名类型、区分大小写的排序,而搜索应该是不区分假名类型和大小写的不区分大小写

这是测试用例:

我会从下面的脚本中假设,我在两个查询中得到相同的结果(预期的排序顺序在第三列中)。基本上一次我按完整单词排序,一次我手动按第一个字母排序,然后是第二个字母,然后是第三个字母。

给定数据库排序规则SQL_Latin1_General_CP1_CI_AS

declare  @temp as table  (title nvarchar(5),  expected int,  script varchar(40) )

set nocount on
INSERT INTO @temp values(N'かか7', 4,'hiragana no accent')
INSERT INTO @temp values(N'がが6',7,'hiragana with accent') 
INSERT INTO @temp values(N'いい5',1,'earlier letter hiragana no accent') 
INSERT INTO @temp values(N'カカ4',3, 'katakana no accent') 
INSERT INTO @temp values(N'ガガ3',6, 'katakana with accent') 
INSERT INTO @temp values(N'かか2',2, 'hiragana no accent') 
INSERT INTO @temp values(N'がが1', 5, 'hiragana with accent')

--BAD
select unicode(left(title,1)) 'bin', * from @temp order by title  
--GOOD
select unicode(left(title,1)) 'bin', * from @temp order by left(title,1),substring(title,2,1), substring(title,3,1)

但是只有第二个版本有效,第一个版本排序不正确:

这似乎与title 字段中的数字有关,因为当我删除它们时,我确实得到了相同的顺序。

declare  @temp as table  (title nvarchar(5),  expected int,  script varchar(40) )

set nocount on
INSERT INTO @temp values(N'かか', 2,'hiragana no accent')
INSERT INTO @temp values(N'がが',3,'hiragana with accent') 
INSERT INTO @temp values(N'いい',1,'earlier letter hiragana no accent') 
INSERT INTO @temp values(N'カカ',2, 'katakana no accent') 
INSERT INTO @temp values(N'ガガ',3, 'katakana with accent') 
INSERT INTO @temp values(N'かか',2, 'hiragana no accent') 
INSERT INTO @temp values(N'がが', 3, 'hiragana with accent')

--GOOD
select unicode(left(title,1)) 'bin', * from @temp order by title  
--GOOD
select unicode(left(title,1)) 'bin', * from @temp order by left(title,1),substring(title,2,1)

查看结果:

有没有人知道原因,可能有解决方案?

【问题讨论】:

  • 拉丁语排序规则不能很好地对非拉丁字符进行排序。如果您需要对非拉丁字符串进行排序,最好使用语言排序规则或二进制排序规则。如果您只希望它在 ORDER BY 中或作为计算列,您可以使用 COLLATE 运算符来帮助完成此操作。
  • 我尝试按所有不同的日语排序规则进行排序,但无济于事。奇怪的是,没有这个数字,它的排序与我预期的完全一样。当手动按第一个、第二个和第三个字母排序时,它也完全按预期工作。
  • 我尝试对 2012 年之后的所有 sql 版本使用所有排序规则进行排序,但没有返回您期望的结果/排序。
  • @lptr 我也是这样做的。我想知道为什么它在字符串中没有这些数字的情况下完美排序(使用正确的日本规则),并且以某种方式与数字中断。如果它被强制使用另一个不区分重音的排序规则,但我不知道如何找出来。
  • 我想知道在最后的第三张截图中,排序是否也从右到左以日文方式读取标题。

标签: sql-server sorting


【解决方案1】:

蛮力方法:检查 SQL Server 中所有支持的排序规则:

create table ##temp(title nvarchar(5),  expected int,  script varchar(40) );

INSERT INTO ##temp values(N'かか7', 4,'hiragana no accent');
INSERT INTO ##temp values(N'がが6',7,'hiragana with accent');
INSERT INTO ##temp values(N'いい5',1,'earlier letter hiragana no accent'); 
INSERT INTO ##temp values(N'カカ4',3, 'katakana no accent');
INSERT INTO ##temp values(N'ガガ3',6, 'katakana with accent'); 
INSERT INTO ##temp values(N'かか2',2, 'hiragana no accent');
INSERT INTO ##temp values(N'がが1', 5, 'hiragana with accent');

还有脚本:

CREATE TABLE result(collation_name NVARCHAR(1000));
DECLARE @collate_name NVARCHAR(1000);
DECLARE @sql NVARCHAR(MAX);

DECLARE c CURSOR FOR
SELECT name FROM sys.fn_helpcollations() /* where name LIKE '%japan%'*/;

OPEN c;
FETCH NEXT FROM c INTO @collate_name;

WHILE @@FETCH_STATUS = 0  
BEGIN  
     SET @sql = REPLACE(
 N'with cte as (
  select bin = unicode(left(title,1)),expected
         ,rn= row_number() over(order by title collate <collate>)
         ,collation = ''<collate>''
   from ##temp 
)
select collation
from cte
where expected = rn GROUP BY collation HAVING COUNT(*) = 7'
     , '<collate>', @collate_name);
     -- debug
     --PRINT @sql;

     INSERT INTO result(collation_name) EXEC (@sql);
     FETCH NEXT FROM c INTO @collate_name;
END 

SELECT * FROM result;

CLOSE c; 
DEALLOCATE c;

db<>fiddle demo

结果:SQL Server 2017 中没有与“预期顺序”匹配的排序规则。

【讨论】:

  • 是的,我也看到了。想知道为什么添加数字会破坏其他正确的工作排序。我的想法是,如果我知道它为什么会破坏排序,也许我可以有一个受过教育的工作。
【解决方案2】:

看起来您的预期订单对日文排序无效。

select case when N'か' COLLATE Japanese_BIN2 < N'カ' COLLATE Japanese_BIN2 then 'True' else 'False' end

否则排序似乎很好。

--CORRECT
select unicode(left(title,1)) 'bin', * from @temp order by title COLLATE Japanese_BIN2
--CORRECT
select unicode(left(title,1)) 'bin', substring(title,2,1), * from @temp order by left(title,2) COLLATE Japanese_BIN2, substring(title,3,1)

两者都生成:

bin    title  expected  script
12356  いい5  1          earlier letter hiragana no accent
12363  かか2  2          hiragana no accent
12363  かか7  4          hiragana no accent
12364  がが1  5          hiragana with accent
12364  がが6  7          hiragana with accent
12459  カカ4  3          katakana no accent
12460  ガガ3  6          katakana with accent

编辑(更多):

declare  @temp as table  (title nvarchar(5) COLLATE Japanese_BIN2,  script varchar(40) )

set nocount on
INSERT INTO @temp values(N'が', 'hiragana with accent') 
INSERT INTO @temp values(N'い', 'earlier letter hiragana no accent') 
INSERT INTO @temp values(N'カ', 'katakana no accent') 
INSERT INTO @temp values(N'ガ', 'katakana with accent') 
INSERT INTO @temp values(N'か', 'hiragana no accent') 

select * from @temp order by title
title   script
い       earlier letter hiragana no accent
か       hiragana no accent
が       hiragana with accent
カ       katakana no accent
ガ       katakana with accent

使用排序规则集,添加尾随数字变得可预测。

declare  @temp as table  (title nvarchar(5) COLLATE Japanese_BIN2,  script varchar(40) )

set nocount on
INSERT INTO @temp values(N'かか7','hiragana no accent')
INSERT INTO @temp values(N'がが6','hiragana with accent') 
INSERT INTO @temp values(N'いい5','earlier letter hiragana no accent') 
INSERT INTO @temp values(N'カカ4','katakana no accent') 
INSERT INTO @temp values(N'ガガ3','katakana with accent') 
INSERT INTO @temp values(N'かか2','hiragana no accent') 
INSERT INTO @temp values(N'がが1','hiragana with accent')

select * from @temp order by title
title   script
いい5    earlier letter hiragana no accent
かか2    hiragana no accent
かか7    hiragana no accent
がが1    hiragana with accent
がが6    hiragana with accent
カカ4    katakana no accent
ガガ3    katakana with accent

编辑 (CI_AI)

不区分大小写/不区分重音的结果,末尾附加数字。

declare  @temp as table  (title nvarchar(5) COLLATE Japanese_90_CI_AI,  script varchar(40) )

set nocount on
INSERT INTO @temp values(N'かか7','hiragana no accent')
INSERT INTO @temp values(N'がが6','hiragana with accent') 
INSERT INTO @temp values(N'いい5','earlier letter hiragana no accent') 
INSERT INTO @temp values(N'カカ4','katakana no accent') 
INSERT INTO @temp values(N'ガガ3','katakana with accent') 
INSERT INTO @temp values(N'かか2','hiragana no accent') 
INSERT INTO @temp values(N'がが1','hiragana with accent')

select * from @temp order by title

平假名和片假名是不同的脚本,可以互换,只有尾随数字排序。

title   script
いい5    earlier letter hiragana no accent
がが1    hiragana with accent
かか2    hiragana no accent
ガガ3    katakana with accent
カカ4    katakana no accent
がが6    hiragana with accent
かか7    hiragana no accent

编辑 (3)

表定义中的排序规则会影响搜索结果。 您甚至可以在排序时动态更改排序规则。

  • Japanese_90_CI_AI:行为(忽略假名,忽略重音)
  • Japanese_90_CI_AS:行为(忽略假名,区分重音符号)
  • Japanese_90_CS_AI:不影响假名类型
  • Japanese_90_CI_AI_KS:行为(忽略重音,排序假名)
  • Japanese_BIN2:行为(排序假名,排序重音)

更换下面的 COLLATE 以更好地了解预期内容

declare  @temp as table  (title nvarchar(5) COLLATE Japanese_90_CI_AS,  script varchar(40) )

set nocount on
INSERT INTO @temp values(N'い','earlier letter hiragana no accent') 
INSERT INTO @temp values(N'か','hiragana no accent')
INSERT INTO @temp values(N'カ','katakana no accent') 
INSERT INTO @temp values(N'ガ','katakana with accent') 
INSERT INTO @temp values(N'が','hiragana with accent');

select a.title, b.title, case when a.title = b.title then 'True' else 'False' end search_equivalent
 from @temp a join @temp b on 1=1

最后我正在寻找一个不区分假名类型、区分大小写的 排序,而搜索应该是假名类型不敏感和大小写 不敏感

For : 搜索应该不区分假名类型和大小写:

declare  @temp as table  (title nvarchar(5) COLLATE Japanese_90_CI_AI,  script varchar(40) )

对于:排序

-- Sorting is predictable using the collation above, predictable, but not what you originally expected.
order by title COLLATE Japanese_90_CS_AI_KS

更多(最终版 - v1)

查看您想要的输出:

  • (把kata当作hira)
  • (区分口音)
  • (unicode 顺序)

因为没有 COLLATION ——来自@Lukasz Szozda 的回答

这是一种可能的解决方法:

declare  @temp as table  (title nvarchar(5),  expected int,  script varchar(40))

set nocount on
INSERT INTO @temp values(N'かか7', 4,'hiragana no accent');
INSERT INTO @temp values(N'がが6',7,'hiragana with accent');
INSERT INTO @temp values(N'いい5',1,'earlier letter hiragana no accent');
INSERT INTO @temp values(N'カカ4',3, 'katakana no accent');
INSERT INTO @temp values(N'ガガ3',6, 'katakana with accent'); 
INSERT INTO @temp values(N'かか2',2, 'hiragana no accent');
INSERT INTO @temp values(N'がが1', 5, 'hiragana with accent');

select * from @temp order by dbo.kata2hira(title) COLLATE Latin1_General_BIN2;
title    expected    script
いい5    1           earlier letter hiragana no accent
かか2    2           hiragana no accent
カカ4    3           katakana no accent
かか7    4           hiragana no accent
がが1    5           hiragana with accent
ガガ3    6           katakana with accent
がが6    7           hiragana with accent
CREATE OR ALTER FUNCTION dbo.Kata2Hira (@kana NVARCHAR(MAX))
RETURNS NVARCHAR(MAX)
WITH SCHEMABINDING
AS
BEGIN

  WITH filler AS (
     select value FROM STRING_SPLIT('1,1,1,1,1,1,1,1,1,1',',')
  ), Hira AS (
    -- 3040 - 309F (Hiragana) 
    SELECT TOP (96) 0x3040 + ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS [N]
    FROM filler x JOIN filler y on 1=1
  ), Kata AS (
    -- 30A0 - 30FF (Katakana)
    SELECT TOP (96) 0x30A0 + ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS [N]
    FROM filler x JOIN filler y on 1=1
  ) 
  SELECT @kana = REPLACE(@kana, NCHAR(Kata.[N]), NCHAR(Hira.[N]))
     FROM Hira JOIN Kata ON NCHAR(Hira.[N]) COLLATE JAPANESE_CS_AS = NCHAR(Kata.[N]) COLLATE JAPANESE_CS_AS

  RETURN @kana;

END;

GO

发布解决方法的想法:

我在检查时只是一个杂项仅供参考,它似乎不是非日语字符所独有的。

将 2 和 1 替换为 い 和 あ,默认排序规则的行为一致。

-- Implied Kana Insensitive vs JAPANESE_CS_AS_KS
select case when N'カ' COLLATE JAPANESE_CS_AS < N'ガ' COLLATE JAPANESE_CS_AS then 'True' else 'False' end as want_true union all
select case when N'カ2' COLLATE JAPANESE_CS_AS < N'ガ1' COLLATE JAPANESE_CS_AS then 'True' else 'False' end union all
select case when N'カb' COLLATE JAPANESE_CS_AS < N'ガa' COLLATE JAPANESE_CS_AS then 'True' else 'False' end union all 
select case when N'カい' COLLATE JAPANESE_CS_AS < N'ガあ' COLLATE JAPANESE_CS_AS then 'True' else 'False' end union all
select case when N'1カい' COLLATE JAPANESE_CS_AS < N'2ガあ' COLLATE JAPANESE_CS_AS then 'True' else 'False' end -- sanity check to make sure we're not reading right to left...haha
want_true
True
False
False
False
True

知道为什么会很有趣……

似是而非的理论:重音敏感排序作为第二遍应用在假名不敏感之后的字段(而不是每个字符)。

【讨论】:

  • 诀窍“假名类型”敏感性。平假名和片假名是不同的脚本,但可以互换。因此,使用假名类型的不敏感排序规则,您会看到没有重音的平假名和片假名将排在有重音的平假名和片假名之前。换句话说,您会看到无重音与有重音的排序,而不是平假名与片假名的排序(如排序规则 BIN/BIN2)
  • 切换到排序规则 Japanese_90_CI_AI/Japanese_90_CI_AS,在考虑和不考虑重音的情况下提供可互换的预期行为。最后添加的非日语字符仍按预期排序。将结果添加到帖子中。
  • 感谢您的所有工作。但是,我使用的默认排序规则似乎很完美(完全按照我的意愿行事),但是一旦添加了数字,它就会变得混乱。为什么添加数字会让人发疯?
  • 您可能需要放弃该任务,并解决默认排序规则并不完美,因为它不能满足您的需求。
  • 我猜你是对的。虽然我还没有找到我正在寻找的真正答案,但由于您的投资,我将赏金奖励给您。感谢您的所有帮助!
猜你喜欢
  • 1970-01-01
  • 2021-07-11
  • 1970-01-01
  • 2014-11-19
  • 2011-10-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-01
相关资源
最近更新 更多