SELECT * FROM tb_name; SELECT * FROM tb_name ORDER BY LEFT(name,1) COLLATE Chinese_PRC_Stroke_CS_AS_KS_WS DESC;
这是一个非常精彩的 SQL 语句组合,它把函数、排序规则和动态排序结合在了一起。
下面我们拆解这个语句,并重点讲清楚第二句里 ORDER BY LEFT(name,1) COLLATE ... 到底在做什么。
一、第一句:普通查询
sql
SELECT * FROM tb_name;
非常简单,就是查询 tb_name 表中的所有列和所有行,按表的物理存储顺序返回(通常是插入顺序,但不保证)。
二、第二句:按首字符的笔画逆序排序
sql
SELECT *FROM tb_nameORDER BY LEFT(name,1) COLLATE Chinese_PRC_Stroke_CS_AS_KS_WS DESC;
逐段拆解
| 部分 | 含义 |
|---|---|
SELECT * | 查询所有列 |
FROM tb_name | 从这张表里查 |
ORDER BY | 按后面的表达式排序 |
LEFT(name,1) | 取 name 列的第一个字符 |
COLLATE Chinese_PRC_Stroke_CS_AS_KS_WS | 临时把这个字符的排序规则改成简体中文笔画排序 |
DESC | 降序排列(笔画多的在前,少的在后) |
整体含义
取
name列的第一个汉字,按简体中文笔画排序规则(区分大小写、重音、假名、全角)进行降序排列,然后输出整张表的所有行。
三、核心重点:LEFT(name,1) COLLATE ... 的作用
1. 没有 COLLATE 会怎样?
sql
-- 假设表里有这些名字张三, 李四, 王五, 赵六, 欧阳锋
sql
ORDER BY LEFT(name,1)
结果: 按第一个字符的默认排序规则(通常是拼音)排序
| 首字 | 拼音 | 排序 |
|---|---|---|
| 李 | li | 1 |
| 欧 | ou | 2 |
| 王 | wang | 3 |
| 张 | zhang | 4 |
| 赵 | zhao | 5 |
2. 加了 COLLATE Chinese_PRC_Stroke_... 后
sql
ORDER BY LEFT(name,1) COLLATE Chinese_PRC_Stroke_CS_AS_KS_WS
结果: 按第一个字符的笔画数排序
| 首字 | 笔画数 | 排序(升序) |
|---|---|---|
| 王 | 4画 | 1 |
| 李 | 7画 | 2 |
| 张 | 7画 | 2(笔顺可能不同) |
| 欧 | 8画 | 4 |
| 赵 | 9画 | 5 |
3. 加了 DESC 后
sql
ORDER BY ... DESC
结果: 笔画多的在前,少的在后
| 首字 | 笔画数 | 排序(降序) |
|---|---|---|
| 赵 | 9画 | 1 |
| 欧 | 8画 | 2 |
| 李 | 7画 | 3 |
| 张 | 7画 | 4 |
| 王 | 4画 | 5 |
四、为什么这个写法很巧妙?
场景:按名字首字的笔画数排序
如果没有 COLLATE,你需要:
创建一个笔画数字段
写一个复杂的函数计算每个字的笔画
或者关联一个笔画字典表
有了 COLLATE:
一行代码搞定
利用 SQL Server 内置的笔画排序规则
实时计算,不需要额外字段
性能代价
| 方面 | 说明 |
|---|---|
| 索引失效 | 用了函数 LEFT(name,1),普通索引用不上 |
| 每行计算 | 每一行都要取首字符、应用排序规则 |
| 大数据量 | 会慢,需要优化(如加计算列并建索引) |
五、完整的执行逻辑示例
假设 tb_name 表有这些数据:
| id | name |
|---|---|
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
| 4 | 赵六 |
| 5 | 欧阳锋 |
| 6 | 周一 |
| 7 | 孙七 |
执行第二句 SQL:
text
第1步:对每一行,取 name 的第一个字符 1 → '张' 2 → '李' 3 → '王' 4 → '赵' 5 → '欧' 6 → '周' 7 → '孙' 第2步:用笔画排序规则计算这些字符的排序权重 张(7画) → 权重值 李(7画) → 权重值(可能和'张'不同,取决于笔顺) 王(4画) → 权重值 赵(9画) → 权重值 欧(8画) → 权重值 周(8画) → 权重值 孙(6画) → 权重值 第3步:按权重降序排列 赵(9画) → 第1行 欧(8画) → 第2行 周(8画) → 第3行 张(7画) → 第4行 李(7画) → 第5行 孙(6画) → 第6行 王(4画) → 第7行 第4步:返回整行数据
六、注意事项和坑
1. 空值和空字符串
sql
-- 如果 name 是 NULLLEFT(NULL,1) → NULL-- NULL 在排序中通常排在最前或最后(取决于数据库设置)-- 如果 name 是空字符串 ''LEFT('',1) → ''(空字符串)2. 非汉字字符
sql
-- 如果 name = '123'LEFT('123',1) → '1'-- 笔画排序规则如何处理数字?按 Unicode 码位排,通常数字在汉字前面3. 多字节字符(Emoji)
sql
-- 如果 name = '😀笑'LEFT('😀笑',1) → '😀'(一个 Emoji)-- 笔画排序规则可能不认识 Emoji,会按码位排序4. 排序规则中的 _CS_AS_KS_WS 对中文的影响
| 后缀 | 对简体中文的影响 |
|---|---|
_CS | 区分大小写(中文没有大小写,不影响) |
_AS | 区分重音(中文没有重音,不影响) |
_KS | 区分假名(日文特有,中文不影响) |
_WS | 区分全角/半角(中文全角字母数字会有影响) |
实际效果: 对于纯汉字,Chinese_PRC_Stroke_CI_AS 和 Chinese_PRC_Stroke_CS_AS_KS_WS 结果一样。
七、对比:有无 COLLATE 的区别
sql
-- 按默认拼音排序SELECT * FROM tb_name ORDER BY name;-- 结果:李四、欧阳锋、孙七、王五、张三、赵六、周一-- 按首字符笔画排序SELECT * FROM tb_name ORDER BY LEFT(name,1) COLLATE Chinese_PRC_Stroke_CI_AS;-- 结果:王五、李四、张三、孙七、欧、周、赵-- 按首字符笔画降序排序(你的语句)SELECT * FROM tb_name ORDER BY LEFT(name,1) COLLATE Chinese_PRC_Stroke_CS_AS_KS_WS DESC;-- 结果:赵六、欧阳锋、周一、张三、李四、孙七、王五
八、总结
| 问题 | 答案 |
|---|---|
| 这个 SQL 在做什么? | 按名字的第一个字的笔画数,从多到少排序 |
LEFT(name,1) | 取第一个字符 |
COLLATE Chinese_PRC_Stroke_... | 临时改用笔画排序规则 |
DESC | 降序(笔画多的在前) |
| 为什么能这么写? | SQL Server 允许在表达式中使用 COLLATE 临时覆盖列的默认排序规则 |
| 有什么代价? | 无法使用索引,大数据量会慢 |
一句话:这个 SQL 巧妙地利用 LEFT() 函数提取首字,再用 COLLATE 临时切换成笔画排序规则,实现了“按名字首字笔画数降序排列”的效果——这是 SQL Server 排序规则灵活性的一个经典应用