← 中文资源

简体中文版本

Unicode 排序规则

排序规则定义文本如何比较与排序——大小写、重音、地区规则;列、字面量与索引之间的规则不一致会破坏相等语义与索引使用。

阅读英文原文

概念定义

文本要定三件事:编码(字节含义)与排序规则(如何比较与排序)。同一 Unicode 字符串在瑞典语与德语地区排序不同;大小写不敏感的规则把 Ada 与 ada 视为同一个值。相等、唯一、ORDER BY 与范围扫描全部由排序规则决定。

工作机制

PostgreSQL 在列或表达式级挂排序规则,提供 libc 与 ICU 两种提供者,非确定性规则甚至把字节不同但规范等价的串视为相等。MySQL 把字符集与排序规则成对绑定在列上。索引内嵌规则:换规则就要重建索引,跨规则比较可能报错或触发转换。

使用场景

需要在数据库层做到大小写/重音不敏感的身份字段、多语言排序、跨引擎或 ICU 升级的模式演进。设计期就显式选定排序规则,别让每次迁移各继承默认值。

常见误区

“存了 UTF-8 就解决了国际化”只解决编码不解决相等与顺序。跨环境规则漂移只在混合操作数相遇时才报错;升级更换 ICU 版本可能改变文本顺序并作废按旧规则建的唯一索引。迁移测试要用真实多语言数据,包括组合重音与全角形式。

代码示例

-- 固定月份的手写透视:CASE 进聚合函数
SELECT
  category,
  SUM(amount) FILTER (WHERE month = '2026-07') AS jul_revenue,
  SUM(amount) FILTER (WHERE month = '2026-08') AS aug_revenue,
  SUM(amount) FILTER (WHERE month = '2026-09') AS sep_revenue,
  SUM(amount) AS total_revenue
FROM monthly_revenue
GROUP BY category
ORDER BY total_revenue DESC;

-- 经典写法对照:CASE WHEN 的条件聚合
SELECT
  category,
  SUM(CASE WHEN month = '2026-07' THEN amount ELSE 0 END) AS jul_revenue,
  SUM(CASE WHEN month = '2026-08' THEN amount ELSE 0 END) AS aug_revenue
FROM monthly_revenue
GROUP BY category;

-- 生成动态列清单(应用层据此拼 SQL)
SELECT DISTINCT month
FROM monthly_revenue
WHERE month BETWEEN '2026-07' AND '2026-09'
ORDER BY month;
package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
)

type Profile struct {
	ID       int64          `json:"id"`
	Nickname sql.NullString `json:"-"`
	Bio      *string        `json:"bio"`
}

type profileJSON struct {
	ID       int64   `json:"id"`
	Nickname *string `json:"nickname"`
	Bio      *string `json:"bio"`
}

func nullString(value sql.NullString) *string {
	if !value.Valid {
		return nil
	}
	return &value.String
}

func (p Profile) MarshalJSON() ([]byte, error) {
	return json.Marshal(profileJSON{
		ID:       p.ID,
		Nickname: nullString(p.Nickname),
		Bio:      p.Bio,
	})
}

func scanProfile(row *sql.Row) (Profile, error) {
	var profile Profile
	err := row.Scan(&profile.ID, &profile.Nickname, &profile.Bio)
	return profile, err
}

func main() {
	encoded, _ := json.Marshal(Profile{ID: 7})
	fmt.Println(string(encoded))
	encoded, _ = json.Marshal(Profile{
		ID:       8,
		Nickname: sql.NullString{String: "ada", Valid: true},
	})
	fmt.Println(string(encoded))
}