select
    avg(distinct score) avg_score
from
    user_action_tb a
    join recommend_tb b on a.user_id = b.rec_user
where
    a.hobby_l = b.rec_info_l

当然可以!你问的是:

牛客网 SQL47:推荐内容准确的用户平均评分

我们来完整、清晰地讲解这道题的 题目解析 + 解题思路 + 易错点 + 优化建议,让你真正“知其然也知其所以然”。

🧩 题目名称(官方描述):

推荐内容准确的用户平均评分

🔗 原题链接:

👉 牛客网 - SQL编程题47

✅ 一、题目要求

从两张表中查询数据:

表结构说明

1. user_action_tb(用户行为表)

user_id

用户 ID

hobby_l

用户的兴趣标签(一级分类)

score

用户对内容的评分(数值)

2. recommend_tb(推荐记录表)

rec_user

被推荐的用户 ID

rec_info_l

推荐的内容标签(一级分类)

🎯 查询目标:

计算 当推荐内容与用户兴趣匹配时,这些用户的平均评分。

即:

  • recommend_tb.rec_user = user_action_tb.user_id
  • recommend_tb.rec_info_l = user_action_tb.hobby_l

👉 这些记录被认为是“推荐准确”的,我们要对它们的 score 求平均值。

✅ 二、示例数据 + 输出分析

输入数据

user_action_tb

1

美食

4

1

旅游

4

2

旅游

5

3

影视

3

recommend_tb

1

旅游

2

旅游

3

音乐

匹配过程分析

我们找满足两个条件的记录:

1

美食 → ❌ 旅游?

1

旅游 → ✔️ 旅游?

✔️ → 参与(score=4)

2

旅游 → ✔️ 旅游?

✔️ → 参与(score=5)

3

影视 → ❌ 音乐?

✅ 只有两条记录匹配:

  • user_id=1, hobby_l='旅游' → score=4
  • user_id=2, hobby_l='旅游' → score=5

👉 平均评分 = (4 + 5) / 2 = 4.5

✅ 三、正确 SQL 写法

SELECT 
    AVG(score) AS avg_score
FROM 
    user_action_tb a
    JOIN recommend_tb b 
        ON a.user_id = b.rec_user 
       AND a.hobby_l = b.rec_info_l;

✅ 四、关键解题思路拆解

步骤 1:确定连接方式

  • 两表通过 user_id = rec_user 关联
  • 必须使用 INNER JOIN(或等价的 JOIN)
  • 因为我们只关心“匹配成功”的情况

步骤 2:添加业务判断条件

  • 不仅要用户相同,还要兴趣标签 == 推荐标签
  • 所以把 a.hobby_l = b.rec_info_l 放在 ON 条件中

⚠️ 注意:不能写成:

JOIN recommend_tb b ON a.user_id = b.rec_user
WHERE a.hobby_l = b.rec_info_l

虽然结果一样,但逻辑上更推荐把所有关联条件放在 ON 中,保持语义清晰。

步骤 3:聚合函数选择

  • 使用 AVG(score) 直接求平均
  • ❌ 不要用 AVG(DISTINCT score)比如两个人都打 4 分,会被当成一个 → 结果错误!

📌 示例:如果两个匹配记录都是 score=4,应该返回 4.0,而不是去重后还是 4 —— 数学上没问题,但样本量错了。

步骤 4:别名注意

  • 输出字段必须是 avg_score(题目要求别名一致)
  • 否则判题系统会报 “答案错误”

✅ 五、常见错误写法对比

LEFT JOIN

会引入不匹配的 NULL 记录,干扰结果

AVG(DISTINCT score)

错误去重,导致统计偏差

WHERE b.rec_info_l = a.hobby_l

单独放

在 LEFT JOIN 下出错,但在 INNER JOIN 中可接受

子查询嵌套

多此一举,此题无需复杂结构

✅ 六、进阶思考(面试加分项)

Q1:如果同一个用户多次被准确推荐,要不要去重?

  • 题意没有说“按用户去重”
  • 示例输出也没有去重 ➡️ 所以 不要去重用户,每条行为都算一次

Q2:如何改为“每个用户的推荐命中率”?

SELECT 
    a.user_id,
    COUNT(CASE WHEN a.hobby_l = b.rec_info_l THEN 1 END) * 1.0 / COUNT(*) AS hit_rate
FROM user_action_tb a
LEFT JOIN recommend_tb b ON a.user_id = b.rec_user
GROUP BY a.user_id;

✅ 总结:解题四步法

1️⃣ 理清业务逻辑

“推荐准确” = 用户ID相同 && 兴趣标签相同

2️⃣ 正确连表

使用 JOIN + 双条件 ON

3️⃣ 聚合计算

AVG(score)

,不加 DISTINCT

4️⃣ 输出格式

别名必须为

avg_score

✅ 最终答案(AC代码)

SELECT 
    AVG(score) AS avg_score
FROM 
    user_action_tb a
    JOIN recommend_tb b 
        ON a.user_id = b.rec_user 
       AND a.hobby_l = b.rec_info_l;