SQL 多维分析 SQL 实战

1次阅读

group by多字段结果对不上,主因是NULL值归为一组及隐式类型转换;窗口函数选型取决于重复值是否占位;where过滤行、having过滤组;join后count突增多因笛卡尔积或null匹配。

SQL 多维分析 SQL 实战

GROUP BY 多字段后聚合结果对不上?检查 NULL 和隐式类型转换

多维分析里 GROUP BY 看似简单,但结果数比预期少,大概率是字段含 NULL 或类型不一致。sql 标准规定 NULL = NULL 为 false,所以所有 NULL 值会被归到同一组——但如果你误以为它们被忽略了,就容易漏数据。

常见错误现象:select region, city, COUNT(*) FROM sales GROUP BY region, city 返回的行数远少于 SELECT DISTINCT region, city FROM sales 的结果数。

  • 先用 SELECT region, city, COUNT(*), COUNT(region), COUNT(city) FROM sales GROUP BY region, city 对比非空计数,确认是否某字段大量为 NULL
  • 若字段来自不同表 JOIN,检查 regionVARchar(10) 还是 CHAR(10):后者带空格,'North ''North',会导致分组分裂
  • postgresqlmysql 8.0+ 支持 GROUP BY 中使用别名,但 sqlite 和旧版 MySQL 不支持,写死字段名更稳妥

窗口函数 ROW_NUMBER() vs RANK() vs DENSE_RANK() 怎么选?看重复值要不要占位

做 Top N、分组排序、连续排名时,三个函数行为差异直接影响业务逻辑。不是“哪个高级”,而是“哪条规则匹配你的需求”。

使用场景举例:按销售额给每个城市的门店排位,要取每城销量前三——这时必须用 ROW_NUMBER();但如果想把并列第二的两家都算作“第二”,后面接“第四”,就得用 RANK()

  • ROW_NUMBER():严格递增,相同值也强制不同序号(如 1,2,2,4)→ 适合唯一分页或抽样
  • RANK():相同值同序号,跳过后续位次(如 1,2,2,4)→ 适合榜单“并列第X名”
  • DENSE_RANK():相同值同序号,不跳位(如 1,2,2,3)→ 适合梯队划分,“前3名”包含所有并列第三
  • 注意:MySQL 5.7 不支持窗口函数,必须升级到 8.0+;SQLite 3.25+ 才支持,旧版本得用自连接模拟

WHERE 和 HAVING 混用报错?记住:过滤原始行用 WHERE,过滤分组结果用 HAVING

HAVING COUNT(*) > 10 却放在 WHERE 后面,报错 Invalid use of group function 是最典型症状。本质是执行顺序问题:SQL 先 WHEREGROUP BYHAVINGHAVING 只能引用分组字段或聚合结果。

容易踩的坑:在 HAVING 里写 HAVING order_date > '2023-01-01' —— 这个字段没出现在 GROUP BY 中,也没聚合,数据库直接拒绝。

  • 如果要筛“2023 年之后下单且人均消费超 500 的城市”,WHERE 控制单条订单时间,HAVING 控制分组后均值:WHERE order_date > '2023-01-01' GROUP BY city HAVING AVG(amount) > 500
  • MySQL 默认开启 sql_mode=only_full_group_by,会严格校验 SELECT 列是否全在 GROUP BY 中;关掉它只是掩盖问题,不是解决逻辑缺陷
  • PostgreSQL 更严格:哪怕只差一个字段,SELECT * + GROUP BY id 都报错,必须显式写出所有非聚合字段

JOIN 后 COUNT(*) 突然变大?警惕笛卡尔积和 NULL 匹配

多维分析常连维度表(如 productscustomers),一加 JOINCOUNT(*) 就暴涨,八成是关联条件没写全,或者主键/外键存在重复或 NULL。

比如 orders JOIN customers ON orders.cust_id = customers.id,若 customers.id 有重复(设计失误)或 orders.cust_idNULL,就会产生意外匹配。

  • 先查关联字段的基数:SELECT COUNT(*), COUNT(DISTINCT cust_id) FROM orders,若二者差距大,说明 cust_id 有大量重复或 NULL
  • LEFT JOIN 时,右表无匹配行会补 NULL,但 COUNT(*) 仍计 1 行;要统计“有客户信息的订单数”,得用 COUNT(customers.id)
  • 多个 JOIN 嵌套时,优先用 INNER JOIN 明确业务约束,避免隐式 CROSS JOIN;不确定关联强度时,先加 LIMIT 10 看中间结果

事情说清了就结束。真正卡住你的,往往不是语法,而是数据本身有没有隐含的重复、空值、类型混杂——多维分析跑不通,先别调 SQL,去 SELECT COUNT(*), COUNT(DISTINCT ...) 看两眼。

text=ZqhQzanResources