unique nulls distinct 将多个null视为不同值,允许重复插入;nulls not distinct 则视多个null为相同值,违反唯一约束。选型取决于业务需求:允许多个空值时用distinct,要求空值也唯一时用not distinct。

UNIQUE NULLS DISTINCT 和 NULLS NOT DISTINCT 怎么选
SQL:2023 新增的 UNIQUE NULLS DISTINCT 和 UNIQUE NULLS NOT DISTINCT 是对唯一约束语义的明确化,不是语法糖,而是解决长期存在的空值歧义问题。核心在于:多个 NULL 算不算“重复”。
多数数据库(如 PostgreSQL、SQL Server)默认行为是 NULLS NOT DISTINCT —— 即两个 NULL 被视为相等,插入重复组合会报错 duplicate key violates unique constraint;而 SQLite、MySQL(严格模式下)则倾向将 NULL 视为未知,不参与比较,实际效果接近 NULLS DISTINCT。
- 显式声明更安全:哪怕你用的是默认行为,也建议写明,避免迁移时语义漂移
- 业务逻辑决定选型:用户注册表中
(email, phone)唯一,允许多人留空电话(NULLS DISTINCT);但员工工号+部门组合必须全局唯一,空部门不允许存在(NULLS NOT DISTINCT) - 注意 DDL 兼容性:
UNIQUE NULLS DISTINCT在 MySQL 8.0.31+、PostgreSQL 15+、SQL Server 2022+ 支持;旧版本会直接报错syntax error near NULLS
GROUP BY 中 ORDER BY 引用非分组列是否合法
SQL:2023 的 F868 特性正式允许在 GROUP BY 查询中,ORDER BY 子句引用未出现在 GROUP BY 列表中的确定性表达式(如主键列、函数依赖列),前提是该列在逻辑上被分组键函数决定。
例如:product_id 是 product 表主键,那么 SELECT product_id, SUM(num) FROM product JOIN product_part ... GROUP BY product_id ORDER BY product_name 在 SQL:2023 下合法——因为 product_name 函数依赖于 product_id。
- 老标准(SQL:2016 及之前)会报错
column "product_name" must appear in the GROUP BY clause or be used in an aggregate function - 启用该特性需数据库支持且开启对应模式:PostgreSQL 16+ 默认启用;MySQL 8.0 需设置
sql_mode包含ONLY_FULL_GROUP_BY的反向逻辑(实际仍受限);SQL Server 2022+ 在兼容级别 160+ 下部分支持 - 别依赖它绕过逻辑设计:如果
product_id不是主键或存在一对多映射,ORDER BY product_name仍会出错或结果不确定
聚合函数里怎么安全用 JSON 数据
SQL:2023 新增了 JSON_ARRAYAGG 和 JSON_OBJECTAGG 的增强语义,支持 NULL ON NULL / ABSENT ON NULL 显式控制空值行为,并允许指定排序子句(ORDER BY)直接嵌套在聚合内部。
例如:SELECT department, JSON_OBJECTAGG(name:value ORDER BY salary DESC NULLS LAST) FROM employees GROUP BY department,能按薪资降序生成每个部门的姓名-薪资映射对象。
- 旧版
JSON_OBJECTAGG(如 PostgreSQL 9.5+、MySQL 5.7+)不支持ORDER BY,必须先子查询排序再聚合,性能差且易出错 -
NULL ON NULL是默认行为,生成{"key": null};ABSENT ON NULL则跳过整个键值对,避免污染结果 - 跨数据库移植风险高:SQLite 尚未实现该特性;Oracle 23c 支持但语法略有差异(用
RETURNING指定类型);务必查目标数据库文档确认支持程度
嵌套分组统计中年/季/月顺序为什么不能颠倒
多级日期分组不是语法问题,而是语义边界问题。GROUP BY YEAR(d), QUARTER(d), MONTH(d) 必须保持年→季→月顺序,否则会出现跨年季度错位(比如把 2023-12 和 2024-01 都归入 Q1)或月份归属错误(如 2023-Q3 里混入 2023-10)。
根本原因是:季度和月份是**相对于年份的偏移量**,不是绝对时间点。单独用 QUARTER(d) 无法区分 2023-Q4 和 2024-Q4;单独用 MONTH(d) 更无法知道属于哪一年的第几季。
- 必须在
WHERE中先限定时间范围,再分组:例如WHERE d >= '2023-01-01' AND d ,否则 <code>GROUP BY会把所有年份的 Q1 都合并 - 避免用字符串拼接当分组键:
CONCAT(YEAR(d), '-Q', QUARTER(d))看似直观,但排序变成字典序(2023-Q10 > 2023-Q2),且丢失数值可计算性 - PostgreSQL 推荐用
DATE_TRUNC('quarter', d)作为分组键,MySQL 用MAKEDATE(YEAR(d), 1) + INTERVAL (QUARTER(d)-1) QUARTER,本质都是还原到季度起点做精确对齐










