题解 | 查询培训指定课程的员工信息

查询培训指定课程的员工信息

https://www.nowcoder.com/practice/a0ef4574056e4a219ee7d651ba82efef

select
    ct.staff_id,
    st.staff_name
from
    staff_tb st
    left join cultivate_tb ct on st.staff_id = ct.staff_id
where
    ct.course like '%course3%'

SELECT DISTINCT
    st.staff_id,
    st.staff_name
FROM
    staff_tb st
INNER JOIN cultivate_tb ct 
    ON st.staff_id = ct.staff_id
WHERE
    FIND_IN_SET('course3', ct.course) > 0;

这道 SQL 题非常典型,我们来 一步一步、清晰地讲解它的解题思路,包括:目的、逻辑、陷阱、优化建议 和 常见错误。

✅ 原始 SQL:

SELECT
    ct.staff_id,
    st.staff_name
FROM
    staff_tb st
    LEFT JOIN cultivate_tb ct ON st.staff_id = ct.staff_id
WHERE
    ct.course LIKE '%course3%'

🧩 一、题目目标(我们要查什么?)

虽然没有明确说明表结构,但从字段名可以推断:

  • staff_tb:员工主表(员工 ID + 姓名)
  • cultivate_tb:培训记录表(员工 ID + 所选课程,可能是逗号分隔的字符串)

👉 目标是:找出所有“报了 course3” 的员工的 ID 和姓名。

🔍 二、逐句解析 SQL

1. SELECT ct.staff_id, st.staff_name

  • 返回两个字段: ct.staff_id:来自培训表的员工 IDst.staff_name:来自员工表的姓名

⚠️ 注意:这里用的是 ct.staff_id 而不是 st.staff_id —— 如果 ct 中为 NULL,会显示 NULL,但其实应该一样。

✅ 更推荐写成 st.staff_id,因为主表更可靠。

2. FROM staff_tb st LEFT JOIN cultivate_tb ct ON st.staff_id = ct.staff_id

看起来是想从员工表出发,关联培训记录。

但是……

🔴 用了 LEFT JOIN 却在 WHERE 中对右表过滤!这是一个经典错误!

❌ 问题来了:

LEFT JOIN cultivate_tb ct ...
WHERE ct.course LIKE '%course3%'

👉 这个 WHERE 条件会 过滤掉 ct.course 不包含 'course3' 或为 NULL 的记录

→ 实际上把 LEFT JOIN 变成了 INNER JOIN 效果!

🔄 对比一下:

LEFT JOIN + WHERE 右表条件

❌ 破坏了 LEFT 的意义,只保留匹配项

LEFT JOIN + ON 中加条件

✅ 才能保留左表全部

3. WHERE ct.course LIKE '%course3%'

  • 使用 LIKE '%course3%' 匹配课程字段中是否包含 'course3'
  • 适用于 course 字段存储的是类似 'course1,course2,course3' 这样的字符串

📌 优点:简单直接

⚠️ 缺点:可能误匹配!

比如:

  • 'course3' ✔️ 正确匹配
  • 'course33' ❌ 也会被匹配到!(因为包含 'course3')
  • 'advanced_course3_beginner' ❌ 也可能误中

✅ 三、正确解法应该是?

✅ 推荐写法(避免 LEFT JOIN 被污染):

SELECT
    st.staff_id,
    st.staff_name
FROM
    staff_tb st
INNER JOIN cultivate_tb ct 
    ON st.staff_id = ct.staff_id
    AND ct.course LIKE '%course3%'

或者保持 LEFT JOIN 风格(不推荐,此处无意义):

-- 不推荐:多此一举
SELECT
    st.staff_id,
    st.staff_name
FROM
    staff_tb st
LEFT JOIN cultivate_tb ct 
    ON st.staff_id = ct.staff_id 
    AND ct.course LIKE '%course3%'
WHERE
    ct.staff_id IS NOT NULL;  -- 手动筛选出有匹配的

👉 本质还是内连接,不如直接写 INNER JOIN

✅ 四、如何更精确匹配 'course3'

当前用 LIKE '%course3%' 容易误判。

✅ 改进建议:

方法 1:使用 FIND_IN_SET(MySQL 推荐!)

SELECT
    st.staff_id,
    st.staff_name
FROM
    staff_tb st
INNER JOIN cultivate_tb ct 
    ON st.staff_id = ct.staff_id
WHERE
    FIND_IN_SET('course3', ct.course) > 0;

✔️ FIND_IN_SET('val', csv) 专门用于判断一个值是否在一个逗号分隔的字符串中

✔️ 要求格式严格:a,b,c,不能有空格

❗ 若有空格需先处理:REPLACE(ct.course, ' ', '')

方法 2:正则表达式(更灵活)

WHERE
    ct.course REGEXP '(^|,)course3(,|$)'

解释:

  • (^|,) → 开头或逗号开始
  • course3 → 精确匹配
  • (,|$) → 后面是逗号或结束

✅ 可防止 course33 被误匹配

✅ 五、完整推荐版本(最终答案)

SELECT DISTINCT
    st.staff_id,
    st.staff_name
FROM
    staff_tb st
INNER JOIN cultivate_tb ct 
    ON st.staff_id = ct.staff_id
WHERE
    FIND_IN_SET('course3', ct.course) > 0;

📌 加 DISTINCT 是为了防止一人多次报名导致重复输出(如果业务允许重复则可不加)

✅ 六、常见错误总结

LEFT JOIN + WHERE 右表非空条件

把 LEFT 变成 INNER,失去意义

LIKE '%course3%'

会误匹配

course33

,

pre_course3

❌ 不去重

同一人多条记录时重复出现

❌ 使用

ct.staff_id

而非

st.staff_id

若右表为空会返回 NULL

✅ 七、一句话总结解题思路

要查询“选修了某门课的员工”,应使用 INNER JOIN 关联员工与课程表,并通过 FIND_IN_SET 或正则精确匹配课程名称,避免模糊搜索带来的误报,同时选择主表字段确保数据完整性。

👏 你已经学会了一类高频面试题:

“如何从一个逗号分隔的字段中查找某个值?”

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务