欢迎光临葬花网
详情描述

一、什么是CTE?

CTE(Common Table Expressions,公共表表达式)是MySQL 8.0引入的重要特性,它允许在查询中定义临时的命名结果集。

-- 基本语法
WITH cte_name [(column_list)] AS (
    SELECT ...
)
SELECT * FROM cte_name;

二、CTE的优势

1. 可读性更强

-- 传统方式:嵌套子查询
SELECT * FROM (
    SELECT * FROM employees WHERE salary > 5000
) AS high_salary_emp;

-- CTE方式:更清晰
WITH high_salary_emp AS (
    SELECT * FROM employees WHERE salary > 5000
)
SELECT * FROM high_salary_emp;

2. 支持递归查询

-- 查询组织结构树
WITH RECURSIVE org_tree AS (
    -- 初始查询:根节点
    SELECT 
        employee_id, 
        name, 
        manager_id,
        1 AS level
    FROM employees 
    WHERE manager_id IS NULL

    UNION ALL

    -- 递归查询:下级员工
    SELECT 
        e.employee_id, 
        e.name, 
        e.manager_id,
        ot.level + 1
    FROM employees e
    JOIN org_tree ot ON e.manager_id = ot.employee_id
)
SELECT * FROM org_tree ORDER BY level, employee_id;

三、CTE的使用模式

模式1:简单CTE(提高可读性)

-- 示例:计算部门平均工资
WITH dept_stats AS (
    SELECT 
        department_id,
        COUNT(*) AS emp_count,
        AVG(salary) AS avg_salary
    FROM employees
    GROUP BY department_id
)
SELECT 
    d.department_name,
    ds.emp_count,
    ds.avg_salary
FROM dept_stats ds
JOIN departments d ON ds.department_id = d.department_id
WHERE ds.avg_salary > 8000;

模式2:多个CTE(链式使用)

WITH 
sales_summary AS (
    SELECT 
        product_id,
        SUM(quantity) AS total_quantity,
        SUM(amount) AS total_amount
    FROM sales
    GROUP BY product_id
),
product_info AS (
    SELECT 
        p.product_id,
        p.product_name,
        p.category,
        s.total_quantity,
        s.total_amount
    FROM products p
    JOIN sales_summary s ON p.product_id = s.product_id
)
SELECT 
    category,
    SUM(total_quantity) AS cat_quantity,
    SUM(total_amount) AS cat_amount
FROM product_info
GROUP BY category
ORDER BY cat_amount DESC;

模式3:递归CTE(层级数据处理)

示例1:生成数字序列

-- 生成1到10的数字
WITH RECURSIVE numbers AS (
    SELECT 1 AS n
    UNION ALL
    SELECT n + 1 FROM numbers WHERE n < 10
)
SELECT * FROM numbers;

示例2:文件路径拆解

WITH RECURSIVE path_parts AS (
    SELECT 
        'a/b/c/d/e.txt' AS full_path,
        1 AS position,
        SUBSTRING_INDEX('a/b/c/d/e.txt', '/', 1) AS part,
        SUBSTRING('a/b/c/d/e.txt', LENGTH(SUBSTRING_INDEX('a/b/c/d/e.txt', '/', 1)) + 2) AS remainder

    UNION ALL

    SELECT 
        full_path,
        position + 1,
        CASE 
            WHEN LOCATE('/', remainder) > 0 
            THEN SUBSTRING_INDEX(remainder, '/', 1)
            ELSE remainder
        END,
        CASE 
            WHEN LOCATE('/', remainder) > 0 
            THEN SUBSTRING(remainder, LENGTH(SUBSTRING_INDEX(remainder, '/', 1)) + 2)
            ELSE ''
        END
    FROM path_parts
    WHERE remainder != ''
)
SELECT * FROM path_parts;

四、CTE的实用场景

场景1:数据清洗和转换

-- 数据清洗:处理缺失值
WITH cleaned_data AS (
    SELECT 
        customer_id,
        COALESCE(name, 'Unknown') AS customer_name,
        COALESCE(email, 'no-email@example.com') AS customer_email,
        CASE 
            WHEN age < 0 THEN NULL
            WHEN age > 120 THEN NULL
            ELSE age
        END AS valid_age
    FROM raw_customers
)
SELECT * FROM cleaned_data WHERE valid_age IS NOT NULL;

场景2:复杂分析查询

-- 计算员工排名和百分比
WITH employee_rank AS (
    SELECT 
        employee_id,
        name,
        salary,
        department_id,
        RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS dept_rank,
        PERCENT_RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) * 100 AS percentile
    FROM employees
)
SELECT 
    e.*,
    d.department_name
FROM employee_rank e
JOIN departments d ON e.department_id = d.department_id
WHERE e.dept_rank <= 3;

场景3:数据分页优化

-- 高效分页查询
WITH ranked_data AS (
    SELECT 
        *,
        ROW_NUMBER() OVER (ORDER BY create_time DESC) AS rn
    FROM large_table
    WHERE status = 'active'
)
SELECT * FROM ranked_data
WHERE rn BETWEEN 1001 AND 1100;

五、CTE性能优化技巧

1. 使用MATERIALIZED强制物化

-- MySQL 8.0.16+ 支持
WITH /*+ MATERIALIZED */ expensive_cte AS (
    SELECT * FROM large_table WHERE complex_condition
)
SELECT * FROM expensive_cte;

2. 避免无限递归

-- 设置递归深度限制
SET SESSION cte_max_recursion_depth = 1000;

WITH RECURSIVE cte AS (
    -- ...
)
SELECT * FROM cte;

3. 合理使用索引

-- 确保CTE查询能使用索引
WITH indexed_cte AS (
    SELECT * FROM orders 
    WHERE order_date >= '2024-01-01'
    -- 确保order_date有索引
)
SELECT * FROM indexed_cte;

六、CTE vs 临时表 vs 子查询

特性 CTE 临时表 子查询
生命周期 单次查询 会话级别 单次查询
性能 优化器可优化 需要手动优化 可能影响性能
可读性 中等
递归支持
复用性 查询内复用 会话内复用 单次使用

七、实战案例:电商数据分析

-- 分析用户购买行为
WITH 
user_orders AS (
    SELECT 
        user_id,
        COUNT(DISTINCT order_id) AS order_count,
        SUM(amount) AS total_spent,
        MIN(order_date) AS first_order_date,
        MAX(order_date) AS last_order_date
    FROM orders
    WHERE order_date >= DATE_SUB(NOW(), INTERVAL 90 DAY)
    GROUP BY user_id
),
user_segments AS (
    SELECT 
        user_id,
        order_count,
        total_spent,
        CASE 
            WHEN order_count >= 10 THEN 'VIP'
            WHEN order_count >= 5 THEN 'Regular'
            WHEN order_count >= 1 THEN 'Casual'
            ELSE 'Inactive'
        END AS user_segment,
        DATEDIFF(NOW(), last_order_date) AS days_since_last_order
    FROM user_orders
)
SELECT 
    user_segment,
    COUNT(*) AS user_count,
    AVG(order_count) AS avg_orders,
    AVG(total_spent) AS avg_spent,
    AVG(days_since_last_order) AS avg_inactive_days
FROM user_segments
GROUP BY user_segment
ORDER BY avg_spent DESC;

八、注意事项

版本要求:MySQL 8.0+ 递归限制:默认最大递归深度1000 性能考虑:复杂CTE可能不如临时表高效 可更新性:CTE通常是只读的

九、最佳实践

命名清晰:使用有意义的CTE名称 适当拆分:复杂的逻辑拆分成多个CTE 性能测试:对比CTE和传统方式的性能 文档注释:为复杂CTE添加注释 避免过深递归:控制递归深度

CTE是MySQL现代查询模式的重要部分,合理使用可以显著提高SQL的可读性和维护性,特别适合复杂的数据分析和报表查询场景。

相关帖子
当越来越多人选择“全抛”,社会应当如何构建新的支持系统与包容文化?
当越来越多人选择“全抛”,社会应当如何构建新的支持系统与包容文化?
老旧小区普遍存在的停车难题,在改造中有哪些创新的解决思路?
老旧小区普遍存在的停车难题,在改造中有哪些创新的解决思路?
2026年城乡居民医保财政补助继续提高,这对个人缴费金额意味着什么?
2026年城乡居民医保财政补助继续提高,这对个人缴费金额意味着什么?
当劳动合同在“三期”内到期,法律对续签或终止有什么特殊规定?
当劳动合同在“三期”内到期,法律对续签或终止有什么特殊规定?
2026年关于高温天气下工作的具体劳动保护措施有哪些新规定?
2026年关于高温天气下工作的具体劳动保护措施有哪些新规定?
2026年关于“带押过户”,买卖双方最容易产生误解的问题有哪些?
2026年关于“带押过户”,买卖双方最容易产生误解的问题有哪些?
当我们谈论“以旧换新”时,是否无意中助长了过度消费和资源浪费的循环?
当我们谈论“以旧换新”时,是否无意中助长了过度消费和资源浪费的循环?
乐山市殡葬服务一条龙办理-殡葬追思会服务,有竞争力的价格
乐山市殡葬服务一条龙办理-殡葬追思会服务,有竞争力的价格
有哪些容易被忽略的PPT操作技巧,能极大提升你的制作速度?
有哪些容易被忽略的PPT操作技巧,能极大提升你的制作速度?
2026年新型隔代育儿补贴形式探索,除现金外还有哪些支持服务?
2026年新型隔代育儿补贴形式探索,除现金外还有哪些支持服务?
如何在家庭与社区中普及祭祀用火的安全知识,有效预防火灾发生?
如何在家庭与社区中普及祭祀用火的安全知识,有效预防火灾发生?
除了查看日期,还有哪些可靠的感官指标能帮助我们判断食物安全性?
除了查看日期,还有哪些可靠的感官指标能帮助我们判断食物安全性?
济宁市殡葬一站式服务|办理白事服务,殡仪殡葬灵堂
济宁市殡葬一站式服务|办理白事服务,殡仪殡葬灵堂
烟台市精准获客@独立网站建设,价格透明
烟台市精准获客@独立网站建设,价格透明
安庆市专业网站建设#安卓app开发,服务可靠
安庆市专业网站建设#安卓app开发,服务可靠
购买不同品牌的新能源汽车,其合作的充电网络费用是否存在明显差别?
购买不同品牌的新能源汽车,其合作的充电网络费用是否存在明显差别?
自贡市办理白事服务-火化入盒,价格合理
自贡市办理白事服务-火化入盒,价格合理
零工工作者在提供服务过程中受伤或发生意外,责任认定与保障机制是怎样的?
零工工作者在提供服务过程中受伤或发生意外,责任认定与保障机制是怎样的?
黄冈市短视频运营推广@企业网站建设公司,收费透明
黄冈市短视频运营推广@企业网站建设公司,收费透明
淄博市殡葬一条龙公司|白事一站式服务,葬礼吊唁
淄博市殡葬一条龙公司|白事一站式服务,葬礼吊唁
黔南品牌网站开发设计#手机app开发,一站式建站服务
黔南品牌网站开发设计#手机app开发,一站式建站服务