sql server中文排序乱序因默认按unicode码点而非拼音/笔画,需在order by或group by中显式指定chinese_prc_pinyin_ci_as等排序规则解决。

直接用 ORDER BY 对中文字段分组后排序,大概率会乱序——不是数据没分对,而是 SQL Server 默认按 Unicode 码点排,和拼音、笔画、常用语序完全不一致。
为什么 GROUP BY 后的中文排序不按拼音走
SQL Server 的默认排序规则(比如 SQL_Latin1_General_CP1_CI_AS)本质是“西式逻辑”,它把汉字当 Unicode 字符处理,张(U+5F20)排在 李(U+674E)前面只是因为码点小,和姓氏常见度、拼音顺序无关。一旦用了 GROUP BY,聚合结果的隐式排序更依赖底层执行计划,不加显式 COLLATE 就等于听天由命。
- 即使列本身定义了
Chinese_PRC_CI_AS,GROUP BY后的SELECT列若参与表达式(如UPPER(name)),排序规则可能降级为Coercible-default,触发隐式转换 - 跨库 JOIN 或临时表场景下,不同对象的排序规则优先级冲突,
ORDER BY可能直接报错:“无法解决排序规则冲突” -
Chinese_PRC_Stroke_CI_AS和Chinese_PRC_PINYIN_CI_AS行为差异大:前者按笔画数,后者才真正按拼音首字母分组再细化
在 ORDER BY 子句里加 COLLATE 最稳妥
这是最轻量、副作用最小的解法。不用改表结构,也不影响索引,只在查询时临时指定语义。
- 写法是:
ORDER BY name COLLATE Chinese_PRC_PINYIN_CI_AS,注意COLLATE必须紧跟在排序字段或表达式之后 - 如果排序字段是聚合结果(如
MAX(name)),COLLATE要加在函数外面:ORDER BY MAX(name) COLLATE Chinese_PRC_PINYIN_CI_AS - 避免用
database_default,它可能继承错误的数据库级排序规则;明确写死Chinese_PRC_PINYIN_CI_AS或Chinese_PRC_Stroke_CI_AS - 测试时用
SELECT name, name COLLATE Chinese_PRC_PINYIN_CI_AS AS pinyin_sort FROM ...看是否真按“阿、八、擦…”排,别只信感觉
GROUP BY 本身也得防排序规则漂移
分组依据字段(GROUP BY xxx)如果排序规则和 ORDER BY 不一致,可能让“同音不同字”的值被拆成多组,比如“张”和“章”在 _CI_AS 下不区分重音,但在 _BIN 下就是两回事。
- 检查分组字段的实际排序规则:
SELECT collation_name FROM sys.columns WHERE object_id = OBJECT_ID('your_table') AND name = 'name' - 如果发现是
SQL_Latin1_General_CP1_CI_AS这类非中文规则,且不能改表,就在GROUP BY里强制:GROUP BY name COLLATE Chinese_PRC_CI_AS - 注意:加
COLLATE后,该字段无法走原有索引(除非索引也是按同一排序规则建的),大数据量时得权衡性能 - 临时表要特别小心:
SELECT name INTO #tmp FROM ...生成的列会继承当前数据库默认排序规则,很可能不是你想要的
真正麻烦的不是语法怎么写,而是同一个中文字段在不同上下文(列定义、变量、函数输出、临时表)里可能套着四五个排序规则;COLLATE 不是万能胶,它是显式声明“这里我要按这个规则算”,漏掉任意一处,排序就可能悄悄跑偏。











