加权汇总须用 group by + sum() 而非 avg(),先标准化再按归一化权重计算得分;topn 排序应选 rank()/dense_rank() 保证并列处理,且需显式计算 score 并建联合索引优化性能。

用 GROUP BY + SUM() 做加权汇总,别直接 AVG()
加权打分本质是「每个指标乘权重后求和」,不是各指标平均。常见错误是把不同量纲的字段(比如点击率 0~1、订单数 0~1000)直接 AVG() 或等权相加,结果完全失真。
正确做法是先标准化(可选),再按业务权重加权:
- 若指标量纲差异大(如
pv和ctr),建议先做 min-max 或 z-score 标准化,否则高量级指标会主导得分 - 权重必须归一化(所有权重和为 1),否则 TopN 排序会受总权重值干扰
- 避免在
SELECT中写死权重,改用CASE WHEN或 JOIN 权重表,方便后续调整
用 ROW_NUMBER() 实现稳定 TopN,慎用 LIMIT
LIMIT 在有并列分数时会随机截断,导致榜单不一致;ROW_NUMBER() 虽然强制去重排序,但可能把同分项拆开——实际业务中往往需要「并列保留、总数可控」,此时该用 RANK() 或 DENSE_RANK():
-
RANK():同分同名次,跳过后续名次(如 1,1,3)——适合强调「段位感」的榜单 -
DENSE_RANK():同分同名次,不跳号(如 1,1,2)——适合展示「前 N 名有多少人」 - 必须搭配
ORDER BY score DESC,且score字段需在SELECT中显式计算,不能只在窗口函数里用
MySQL 8.0+ 和 PostgreSQL 的写法差异点
两者都支持窗口函数,但初始化方式和 NULL 处理逻辑不同:
- MySQL 8.0+ 要求子查询或 CTE 先算出
score,再套窗口函数;直接在窗口ORDER BY里写表达式会报错 - PostgreSQL 允许在
OVER()中直接写ORDER BY click_cnt * 0.4 + ctr * 0.6,但要注意ctr为 NULL 时整行得分变 NULL——得用COALESCE(ctr, 0) - 如果数据量大(千万级),记得给参与分组和排序的字段(如
user_id,score)建联合索引,否则ORDER BY ... LIMIT可能全表扫描
一个可直接跑的 PostgreSQL 示例
WITH scored AS (
SELECT
user_id,
COALESCE(pv, 0) * 0.3 +
COALESCE(ctr, 0) * 0.5 +
COALESCE(order_cnt, 0) * 0.2 AS score
FROM user_behavior
WHERE dt = '2024-06-01'
)
SELECT
user_id,
ROUND(score, 3) AS final_score,
RANK() OVER (ORDER BY score DESC) AS rank_no
FROM scored
WHERE score IS NOT NULL
ORDER BY rank_no
LIMIT 10;
注意这里用了 RANK() 而非 ROW_NUMBER(),因为运营需要知道「Top10 榜单实际有 12 人」;WHERE score IS NOT NULL 是硬性过滤,避免 NULL 占掉一个榜单名额;ROUND() 不仅为了可读性,还防止浮点误差影响排序稳定性。
真实场景里,权重常来自配置表或参数化输入,而非常量。一旦权重变成动态,就得警惕 SQL 注入和执行计划缓存失效——这时候该考虑用应用层拼装,而不是硬写进 SQL。










