DeepSeek辅助生成的PostgreSQL 查询优化实战幻灯片脚本

经过比较,代码用formatter_style="colorful"paragraph_config={"font": "Milky Han Mono SC"}设置效果较好。

python 复制代码
from manim import *
from manim_slides import Slide

# 配置中文字体,请根据系统环境调整
config.tex_template.add_to_preamble(r"\usepackage{ctex}")
config.tex_template.add_to_preamble(r"\usepackage{xcolor}")

class QueryOptimization(Slide):
    """PostgreSQL 查询优化专题幻灯片 - 查询语句写法规范与技巧"""
    def construct(self):
        # ---------- 标题页 ----------
        title = Text("PostgreSQL 查询优化实战", font_size=48, color=BLUE)
        subtitle = Text("第二专题:查询语句写法规范与技巧", font_size=36, color=GRAY)
        authors = Text("少查·快连·精索·常析·避坑", font_size=28, color=GREEN)
        VGroup(title, subtitle, authors).arrange(DOWN, buff=0.5)
        self.play(Write(title))
        self.play(FadeIn(subtitle, shift=UP))
        self.play(FadeIn(authors, shift=UP))
        self.wait(1)
        self.next_slide()

        # 清除当前画面
        self.clear()

        # ---------- 1. WHERE vs HAVING 对比 ----------
        where_title = Text("1. 优先使用WHERE而非HAVING", font_size=40, color=YELLOW).to_edge(UP)
        self.play(Write(where_title))

        # WHERE示例 - 修复参数
        where_code = Code(
            code_string='''
-- 正: 正确:使用WHERE在聚合前过滤
SELECT status, COUNT(*) 
FROM users 
WHERE created_at >= '2024-01-01'  -- 先过滤
GROUP BY status;
            ''',
            language="sql",
            background="window",
            add_line_numbers=False,formatter_style="colorful",
            paragraph_config={"font_size": 20,"font": "Milky Han Mono SC"}
        ).scale(0.8).shift(UP*0.5)

        # HAVING示例 - 修复参数
        having_code = Code(
            code_string='''
-- 误: 低效:使用HAVING在聚合后过滤
SELECT status, COUNT(*) 
FROM users 
GROUP BY status
HAVING status = 1;  -- 后过滤,浪费资源
            ''',
            language="sql",
            background="window",
            add_line_numbers=False,formatter_style="colorful",
            paragraph_config={"font_size": 20,"font": "Milky Han Mono SC"}
        ).next_to(where_code, DOWN, buff=0.5).scale(0.8)

        self.play(FadeIn(where_code, shift=LEFT))
        self.play(FadeIn(having_code, shift=RIGHT))

        note = Text(
            "WHERE: 在聚合前过滤行\nHAVING: 在聚合后过滤分组",
            font_size=24, color=BLUE
        ).to_edge(DOWN)
        self.play(Write(note))
        self.wait(2)
        self.next_slide()

        # ---------- 2. 避免SELECT * ----------
        self.clear()
        select_title = Text("2. 避免使用SELECT *", font_size=40, color=YELLOW).to_edge(UP)
        self.play(Write(select_title))

        # 不好的写法
        select_star = Code(
            code_string='''
-- 误: 糟糕:返回所有列
SELECT * FROM users 
WHERE status = 1;

-- 问题:
-- 1. 可能返回不需要的大字段(TEXT/JSON)
-- 2. 无法使用覆盖索引
-- 3. 表结构变更影响应用程序
            ''',
            language="sql",
            background="window",
            add_line_numbers=False,formatter_style="colorful",
            paragraph_config={"font_size": 18,"font": "Milky Han Mono SC"}
        ).scale(0.7).shift(LEFT*3+UP*0.5)

        # 好的写法
        select_specific = Code(
            code_string='''
-- 正: 优秀:只返回需要的列
SELECT id, email, created_at 
FROM users 
WHERE status = 1;

-- 优点:
-- 1. 减少网络传输
-- 2. 可能使用覆盖索引
-- 3. 程序更健壮
            ''',
            language="sql",
            background="window",
            add_line_numbers=False,formatter_style="colorful",
            paragraph_config={"font_size": 18,"font": "Milky Han Mono SC"}
        ).scale(0.7).shift(RIGHT*3+UP*0.5)

        self.play(FadeIn(select_star, shift=LEFT))
        self.play(FadeIn(select_specific, shift=RIGHT))

        # 添加覆盖索引说明
        cover_idx = Code(
            code_string='''
-- 覆盖索引示例
CREATE INDEX idx_user_status_covering 
ON users(status) INCLUDE (id, email);

-- 查询可以直接从索引获取数据,无需回表
            ''',
            language="sql",
            background="rectangle",
            add_line_numbers=False,formatter_style="colorful",
            paragraph_config={"font_size": 16,"font": "Milky Han Mono SC"}
        ).scale(0.7).to_edge(DOWN)

        self.play(FadeIn(cover_idx, shift=UP))
        self.wait(2)
        self.next_slide()

        # ---------- 3. 善用LIMIT分页 ----------
        self.clear()
        limit_title = Text("3. 善用LIMIT分页与游标", font_size=40, color=YELLOW).to_edge(UP)
        self.play(Write(limit_title))

        # 传统分页问题
        offset_pagination = Code(
            code_string='''
-- 误: 深分页性能问题
SELECT * FROM users 
ORDER BY id 
LIMIT 10 OFFSET 10000;  
-- OFFSET 10000 仍然需要扫描10010行
            ''',
            language="sql",
            background="window",
            add_line_numbers=False,formatter_style="colorful",
            paragraph_config={"font_size": 20,"font": "Milky Han Mono SC"}
        ).scale(0.8).shift(UP*0.5)

        # 游标分页优化
        cursor_pagination = Code(
            code_string='''
-- 正: 游标分页(键集分页)
SELECT * FROM users 
WHERE id > 10000  -- 记住上一页最后一条的ID
ORDER BY id 
LIMIT 10;

-- 优点:始终只扫描10行,性能稳定
            ''',
            language="sql",
            background="window",
            add_line_numbers=False,formatter_style="colorful",
            paragraph_config={"font_size": 20,"font": "Milky Han Mono SC"}
        ).next_to(offset_pagination, DOWN, buff=0.5).scale(0.8)

        self.play(FadeIn(offset_pagination, shift=LEFT))
        self.play(FadeIn(cursor_pagination, shift=RIGHT))

        # 性能对比
        perf_note = Text(
            "OFFSET 10000: 扫描10010行 | 游标分页: 扫描10行",
            font_size=24, color=GREEN
        ).to_edge(DOWN)
        self.play(Write(perf_note))
        self.wait(2)
        self.next_slide()

        # ---------- 4. JOIN优化技巧 ----------
        self.clear()
        join_title = Text("4. JOIN查询优化技巧", font_size=40, color=YELLOW).to_edge(UP)
        self.play(Write(join_title))

        # 先过滤再JOIN
        join_example = Code(
            code_string='''
-- 正: 先缩小数据集再JOIN
SELECT u.id, u.email, o.order_amount
FROM (
    SELECT * FROM users 
    WHERE status = 1  -- 先过滤用户
) u
JOIN (
    SELECT * FROM orders 
    WHERE created_at > now() - interval '30 days'  -- 只关联最近30天订单
) o ON u.id = o.user_id;

-- 等价改写(优化器可能自动优化,但显式写更清晰)
            ''',
            language="sql",
            background="window",
            add_line_numbers=False,formatter_style="colorful",
            paragraph_config={"font_size": 18,"font": "Milky Han Mono SC"}
        ).scale(0.7).shift(UP*0.5)

        # JOIN索引要求
        join_index = Code(
            code_string='''
-- JOIN字段必须建索引
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_orders_created_at ON orders(created_at);

-- 小表驱动大表
-- 优化器通常会自动选择,但了解原理有帮助
            ''',
            language="sql",
            background="window",
            add_line_numbers=False,formatter_style="colorful",
            paragraph_config={"font_size": 18,"font": "Milky Han Mono SC"}
        ).next_to(join_example, DOWN, buff=0.3).scale(0.7)

        self.play(FadeIn(join_example, shift=UP))
        self.play(FadeIn(join_index, shift=DOWN))

        join_note = Text(
            "原则:小结果集驱动大结果集 | JOIN列务必索引",
            font_size=24, color=BLUE
        ).to_edge(DOWN)
        self.play(Write(join_note))
        self.wait(2)
        self.next_slide()

        # ---------- 5. 查询写法规范总结 ----------
        self.clear()
        summary_title = Text("查询写法五大黄金法则", font_size=44, color=YELLOW).to_edge(UP)
        self.play(Write(summary_title))

        rules = VGroup(
            Text("1️⃣ 过滤先行: WHERE优先于HAVING,先缩小数据集", font_size=28),
            Text("2️⃣ 列显式化: 杜绝SELECT *,只取所需字段", font_size=28),
            Text("3️⃣ 分页优化: 用游标分页替代OFFSET深分页", font_size=28),
            Text("4️⃣ JOIN精简: 先过滤再JOIN,确保关联字段有索引", font_size=28),
            Text("5️⃣ 批量操作: 避免循环单条SQL,使用批量DML", font_size=28),
        ).arrange(DOWN, aligned_edge=LEFT, buff=0.3).shift(UP*0.5)

        for rule in rules:
            self.play(Write(rule, lag_ratio=0.1))
            self.wait(0.3)

        # 性能对比示例
        batch_example = Code(
            code_string='''
-- 误: 低效:循环单条插入
BEGIN;
INSERT INTO logs VALUES (1, 'msg1');
INSERT INTO logs VALUES (2, 'msg2');
INSERT INTO logs VALUES (3, 'msg3');
COMMIT;

-- 正: 高效:批量插入
INSERT INTO logs VALUES 
    (1, 'msg1'),
    (2, 'msg2'),
    (3, 'msg3');
            ''',
            language="sql",
            background="rectangle",
            add_line_numbers=False,formatter_style="colorful",
            paragraph_config={"font_size": 16,"font": "Milky Han Mono SC"}
        ).scale(0.6).to_edge(DOWN)

        self.play(FadeIn(batch_example, shift=UP))
        self.wait(3)

        # 下一专题预告
        self.clear()
        next_topic = Text("下一专题预告", font_size=48, color=BLUE)
        topic_name = Text("表结构设计基础规范", font_size=36, color=GREEN)
        topic_points = VGroup(
            Text("• 遵循三范式但适度反范式", font_size=28),
            Text("• 合理选择数据类型", font_size=28),
            Text("• 主键必设且用INT/UUID", font_size=28),
        ).arrange(DOWN, aligned_edge=LEFT, buff=0.2)

        VGroup(next_topic, topic_name, topic_points).arrange(DOWN, buff=0.5)
        self.play(
            Write(next_topic),
            Write(topic_name),
            *[Write(point) for point in topic_points]
        )
        self.wait(3)


class PreviousTopicReview(Slide):
    """快速回顾第一专题核心内容"""
    def construct(self):
        title = Text("第一专题回顾: 索引优化", font_size=48, color=YELLOW).to_edge(UP)
        self.play(Write(title))

        # 核心要点回顾
        points = VGroup(
            Text("🔑 建得准: 复合索引设计 (等值列在前, 范围/排序在后)", font_size=28),
            Text("🔑 用得对: 避免函数、隐式转换、左模糊", font_size=28),
            Text("🔑 管得勤: EXPLAIN ANALYZE验证, 删除冗余索引", font_size=28),
            Text("🔑 B+树特点: 叶子存数据, 双向链表, 高度平衡", font_size=28),
        ).arrange(DOWN, aligned_edge=LEFT, buff=0.3).shift(UP*1)

        for point in points:
            self.play(Write(point, lag_ratio=0.1))
            self.wait(0.2)

        # 快速示例
        example = Code(
            code_string='''
-- 优秀复合索引示例
CREATE INDEX idx_users_status_created 
ON users(status, created_at DESC);

-- 高效查询
EXPLAIN ANALYZE
SELECT id, email 
FROM users 
WHERE status = 1 
ORDER BY created_at DESC 
LIMIT 10;
            ''',
            language="sql",
            background="window",
            add_line_numbers=False,formatter_style="colorful",
            paragraph_config={"font_size": 20,"font": "Milky Han Mono SC"}
            
        ).scale(0.7).to_edge(DOWN)

        self.play(FadeIn(example, shift=UP))
        self.wait(3)


# 运行命令:
# manim -pql query_slides.py QueryOptimization
# manim -pql query_slides.py PreviousTopicReview
相关推荐
祢真伟大2 小时前
DM8单库使用DMDRS数据同步到dpc-步骤三
数据库
知识分享小能手2 小时前
SQL Server 2019入门学习教程,从入门到精通,SQL Server 2019 存储过程与自定义函数 — 语法知识点及使用方法详解(15)
数据库·学习·sqlserver
dinga198510262 小时前
MySQL 批量删除海量数据的几种方法
数据库·mysql
Aric_Jones2 小时前
博客RBAC权限模型与安全认证全解析
数据库·安全·oracle
heimeiyingwang2 小时前
企业级知识库构建:从数据清洗到向量检索
大数据·人工智能·机器学习
globaldomain2 小时前
立海世纪:.com和.net域名哪个更适合你的网站
大数据·前端·人工智能·新媒体运营·国外域名·域名注册
媒体人8883 小时前
孟庆涛:生成式引擎优化(GEO)的投毒攻击防御策略研究
大数据·人工智能·搜索引擎·生成式引擎优化·geo优化
志栋智能3 小时前
AI驱动的自动化运维机器人:从“数字劳动力”到“智能协作者”的进化
大数据·运维·网络·人工智能·机器人·自动化
wfsm3 小时前
有向图的状态转换
数据库