跳到主要内容

SQL228. 使用join查询方式找出没有分类的电影id以及名称

题目描述

使用 JOIN 查询方式,找出没有分类的电影 id 以及电影名称。

表结构

表1:film(电影信息表)

CREATE TABLE film (
film_id SMALLINT(5) PRIMARY KEY COMMENT '电影id',
title VARCHAR(255) COMMENT '电影名称',
description TEXT COMMENT '电影描述信息'
);
字段名类型说明
film_idSMALLINT(5)电影id,主键
titleVARCHAR(255)电影名称
descriptionTEXT电影描述信息

表2:category(类别表)

CREATE TABLE category (
category_id TINYINT(3) PRIMARY KEY COMMENT '电影分类id',
name VARCHAR(25) COMMENT '电影分类名称',
last_update TIMESTAMP COMMENT '最后更新时间'
);
字段名类型说明
category_idTINYINT(3)电影分类id,主键
nameVARCHAR(25)电影分类名称
last_updateTIMESTAMP最后更新时间

表3:film_category(电影分类关联表)

CREATE TABLE film_category (
film_id SMALLINT(5) COMMENT '电影id',
category_id TINYINT(3) COMMENT '电影分类id',
last_update TIMESTAMP COMMENT '最后更新时间',
PRIMARY KEY (film_id, category_id)
);
字段名类型说明
film_idSMALLINT(5)电影id
category_idTINYINT(3)电影分类id
last_updateTIMESTAMP最后更新时间

示例数据

film

film_idtitledescription
1ACADEMY DINOSAUR...
2ACE GOLDFINGER...
3ADAPTATION HOLES...

film_category

film_idcategory_idlast_update
162006-02-14 21:07:09
2112006-02-14 21:07:09

预期输出

film_idtitle
3ADAPTATION HOLES

电影 3(ADAPTATION HOLES)在 film_category 表中没有对应记录,因此是没有分类的电影。


解题思路

第一步:理解需求

题目要求找出"没有分类"的电影。这意味着这些电影在 film 表中存在,但在 film_category 关联表中没有对应的记录

第二步:分析表结构

三张表的关系:

  • film:存储所有电影的基本信息
  • category:存储所有分类的名称
  • film_category:存储电影和分类的关联关系

要判断一部电影是否有分类,只需检查它是否存在于 film_category 表中。

第三步:确定 SQL 方案

核心思路是左外连接 + NULL 判断

  1. filmLEFT JOIN film_category
  2. 没有分类的电影在 film_category 一侧会匹配为 NULL
  3. 筛选 film_category.film_id IS NULL 的记录

第四步:编写并验证 SQL

使用 LEFT JOIN 保证 film 表中的所有电影都被保留,即使它们在 film_category 中没有匹配项。


完整 SQL 实现

-- 方法一:LEFT JOIN + IS NULL —— 题目要求的 JOIN 方式
SELECT
f.film_id,
f.title
FROM film AS f
LEFT JOIN film_category AS fc
ON f.film_id = fc.film_id
WHERE fc.film_id IS NULL;

-- 方法二:NOT IN —— 另一种思路(非 JOIN 方式,供参考)
SELECT film_id, title
FROM film
WHERE film_id NOT IN (
SELECT film_id FROM film_category WHERE film_id IS NOT NULL
);

-- 方法三:NOT EXISTS —— 效率通常优于 NOT IN,尤其在数据量大时
SELECT film_id, title
FROM film AS f
WHERE NOT EXISTS (
SELECT 1 FROM film_category AS fc WHERE fc.film_id = f.film_id
);

解法详解

方法一:LEFT JOIN + IS NULL(推荐,符合题意)

SELECT f.film_id, f.title
FROM film AS f
LEFT JOIN film_category AS fc ON f.film_id = fc.film_id
WHERE fc.film_id IS NULL;

执行过程

  1. LEFT JOINfilm 表的每一行与 film_category 表匹配
  2. 电影 1 和 2 在 film_category 中有匹配记录,fc.film_id 有值
  3. 电影 3 在 film_category 中没有匹配,fc.film_idNULL
  4. WHERE fc.film_id IS NULL 筛选出电影 3

LEFT JOIN 图解

film                    film_category (LEFT JOIN 结果)
+----+---------------+ +----+----+ +----+---------------+----+------+
| id | title | | id |cat | | id | title |cat_id|
+----+---------------+ +----+----+ +----+---------------+------+
| 1 | ACADEMY DINO |→ | 1 | 6 | | 1 | ACADEMY DINO | 6 |
| 2 | ACE GOLDFINGER|→ | 2 | 11 | | 2 | ACE GOLDFINGER| 11 |
| 3 | ADAPTATION... |→ 无匹配 | 3 | ADAPTATION... | NULL |
+----+---------------+ +----+---------------+------+
↑ 筛选 IS NULL

方法二:NOT IN

SELECT film_id, title FROM film
WHERE film_id NOT IN (
SELECT film_id FROM film_category WHERE film_id IS NOT NULL
);

注意:子查询中的 WHERE film_id IS NOT NULL 非常重要!如果 film_category.film_id 存在 NULL 值,NOT IN 会返回空结果(因为 x NOT IN (NULL, ...) 在 SQL 三值逻辑中结果为 UNKNOWN)。

方法三:NOT EXISTS

SELECT film_id, title FROM film AS f
WHERE NOT EXISTS (
SELECT 1 FROM film_category AS fc WHERE fc.film_id = f.film_id
);

NOT EXISTS 不会受 NULL 值影响,且在大数据量时通常比 NOT IN 性能更好,因为数据库可以更早停止扫描。


知识点总结

1. LEFT JOIN(左外连接)

LEFT JOIN 返回左表的所有记录,右表没有匹配时补 NULL。

SELECT * FROM A
LEFT JOIN B ON A.id = B.id;
A.idA.nameB.idB.value
1Alice1100
2BobNULLNULL

2. JOIN 类型对比

JOIN 类型说明
INNER JOIN只返回两表匹配的记录
LEFT JOIN返回左表全部,右表不匹配补 NULL
RIGHT JOIN返回右表全部,左表不匹配补 NULL
FULL JOIN返回两表全部,不匹配侧补 NULL

3. IS NULL 与 = NULL

在 SQL 中,必须使用 IS NULL 判断空值,不能用 = NULL。因为 NULL = NULL 的结果是 UNKNOWN,不是 TRUE。


易错点总结

1. 误用 INNER JOIN

-- 错误!INNER JOIN 只会返回有分类的电影
SELECT f.film_id, f.title
FROM film AS f
INNER JOIN film_category AS fc ON f.film_id = fc.film_id; -- 没有分类的电影被过滤掉了

2. NOT IN 遇到 NULL 值

-- 危险写法!如果 film_category.film_id 有 NULL,结果为空
SELECT * FROM film
WHERE film_id NOT IN (SELECT film_id FROM film_category); -- 不要省略 WHERE 子查询的 IS NOT NULL

3. 混淆 LEFT JOIN 的方向

-- 错误!方向反了,结果会不同
SELECT * FROM film_category AS fc -- 这是左表!
LEFT JOIN film AS f ON f.film_id = fc.film_id
WHERE f.film_id IS NULL;
-- 这会返回 film_category 中有但 film 中没有的记录(通常是空结果)

扩展思考

  • 如何找出有分类的电影? 使用 INNER JOINLEFT JOIN + WHERE fc.film_id IS NOT NULL
  • 如何统计每类电影的数量? JOINGROUP BY category.name, COUNT(*)
  • 性能考虑:确保 film_category.film_id 有索引,LEFT JOINNOT EXISTS 都能高效执行。

相关题目

加载评论中...