作品列表页要「作品 + 分类名」,就得写一句二表 LEFT JOIN——可这句 SQL 你在首页推荐位、列表页、详情页、后台列表里要抄四遍,改一次得改四处。本章教你用视图把这段 SQL 打包成一个名字,以后一句 SELECT * FROM v_article_list 就够;顺带入门存储过程,认识另一种「把逻辑存进数据库」的工具。
CREATE VIEW v_tang_article AS
SELECT id, title, author, views
FROM article
WHERE dynasty = '唐';
建好之后,它就真的像一张表。直接 SELECT 它:
SELECT * FROM v_tang_article;
SELECT title, views
FROM v_tang_article
WHERE views > 250
ORDER BY views DESC;
-- 改定义:整体替换,不是「加一列」
ALTER VIEW v_tang_article AS
SELECT id, title, author, dynasty, views, likes
FROM article
WHERE dynasty = '唐' AND status = 1;
-- 删视图
DROP VIEW IF EXISTS v_tang_article;
CREATE OR REPLACE VIEW `v_article_list` AS
SELECT a.`id`, a.`title`, a.`author`, a.`dynasty`,
c.`name` AS `category_name`,
a.`cover`, a.`summary`, a.`views`, a.`likes`, a.`is_recommend`, a.`created_at`
FROM `article` a
LEFT JOIN `category` c ON a.`category_id` = c.`id`
WHERE a.`status` = 1;
于是「作品列表页」要的那 11 个字段,一句查询就全出来了——再也不用在 Python 里拼 JOIN:
SELECT id, title, author, dynasty, category_name, views, likes
FROM v_article_list
ORDER BY views DESC;
首页推荐位只要在视图上加一个条件:
SELECT id, title, category_name, views
FROM v_article_list
WHERE is_recommend = 1
ORDER BY views DESC
LIMIT 6;
INSERT INTO v_article_list (title, author) VALUES ('新诗', '无名氏');
DELIMITER $$
CREATE PROCEDURE p_count_by_dynasty(IN p_dynasty VARCHAR(20))
BEGIN
SELECT COUNT(*) AS 篇数
FROM article
WHERE dynasty = p_dynasty;
END $$
DELIMITER ;
调用它,参数写在小括号里:
CALL p_count_by_dynasty('唐');
用完删掉它(存储过程也是要清理的):
DROP PROCEDURE IF EXISTS p_count_by_dynasty;
| 方式 | 本质 | 适合 | 不适合 |
|---|---|---|---|
| 普通查询 | 现场写的 SELECT | 一次性、临时的取数 | 同一段长 SQL 到处复制 |
| 视图 | 存起来的「一条 SELECT」 | 口径固定的复合查询(作品列表) | 需要传参数的场景 |
| 存储过程 | 存起来的「一段可执行脚本」 | 多步逻辑、要参数、要分支循环 | 简单查询;需要跨数据库移植 |
-- 第 1 题:作品 + 书评条数
-- 用 LEFT JOIN 保证没书评的作品也在;条数用 COUNT(c.id)(COUNT(*) 会把 NULL 那行也数成 1)
CREATE OR REPLACE VIEW v_article_comment_stat AS
SELECT a.id, a.title, COUNT(c.id) AS comment_count
FROM article a
LEFT JOIN comment c ON c.article_id = a.id AND c.status = 1
GROUP BY a.id, a.title;
SELECT * FROM v_article_comment_stat ORDER BY id;
-- 第 2 题:按分类名统计作品数
DELIMITER $$
CREATE PROCEDURE p_count_by_category(IN p_cat VARCHAR(30))
BEGIN
SELECT COUNT(*) AS 篇数
FROM article a
JOIN category c ON a.category_id = c.id
WHERE c.name = p_cat;
END $$
DELIMITER ;
CALL p_count_by_category('唐诗');
DROP PROCEDURE IF EXISTS p_count_by_category;
-- 第 3 题(红影赏析,表结构不同,思路一致)
CREATE OR REPLACE VIEW v_movie_list AS
SELECT m.id, m.title, m.genre, m.rating, d.name AS director_name
FROM movie m
LEFT JOIN director d ON m.director_id = d.id;
第 1 题最容易错的地方是写成 COUNT(*):左连接后,没书评的作品会留一行 c.* 全为 NULL 的记录,COUNT(*) 会把它算成 1。写 COUNT(c.id) 才会得到 0。这就是第 4 章讲过的那条规则——COUNT(列) 忽略 NULL。