WITH语句还是子查询?

目录

一、WITH语句的基本语法

二、使用WITH语句和子查询的对比

使用WITH的SQL

使用子查询的SQL

三、总结


SQL中的WITH语句,也称为公用表表达式(Common Table Expressions,简称CTE),允许你定义一个临时的结果集,这个结果集可以在一个或多个SELECTINSERTUPDATEDELETE语句中被引用。CTE在逻辑上类似于一个子查询,但它可以包含多个SELECT语句,并且可以进行递归查询。

一、WITH语句的基本语法

WITH cte_name (column1, column2, ...)
AS
(
    -- CTE的查询定义SELECT column1, column2, ...
    FROM ...
)
-- 主查询SELECT*FROM cte_name;

二、使用WITH语句和子查询的对比

假设有以下需求:有一个 orders表,其中包含订单ID、客户ID和订单日期,还有一个 customers表,包含客户ID和客户名称。我们想要查询每个客户在2022年的总订单金额,并且只关注那些至少有3个订单的客户

使用WITH的SQL

WITH OrderDetails AS (
    SELECT
        o.order_id,
        o.customer_id,
        o.order_date,
        SUM(o.amount) OVER (PARTITIONBY o.customer_id) AS total_amount
    FROM
        orders o
    WHERE
        o.order_date BETWEEN'2022-01-01'AND'2022-12-31'
),
CustomerOrders AS (
    SELECT
        customer_id,
        COUNT(*) AS order_count
    FROM
        OrderDetails
    GROUPBY
        customer_id
    HAVINGCOUNT(*) >=3
)
SELECT
    c.customer_id,
    c.name,
    od.total_amount
FROM
    CustomerOrders co
JOIN
    customers c ON co.customer_id = c.customer_id
JOIN
    OrderDetails od ON co.customer_id = od.customer_id
ORDERBY
    od.total_amount DESC;

使用子查询的SQL

SELECT
    c.customer_id,
    c.name,
    (SELECTSUM(amount) FROM orders o WHERE o.order_date BETWEEN'2022-01-01'AND'2022-12-31'AND o.customer_id = c.customer_id) AS total_amount
FROM
    customers c
WHEREEXISTS (
        SELECT1FROM orders o
        WHERE
            o.order_date BETWEEN'2022-01-01'AND'2022-12-31'AND o.customer_id = c.customer_id
        GROUPBY
            o.customer_id
        HAVINGCOUNT(*) >=3
    )
ORDERBY
    total_amount DESC;

三、总结

1.WITH语句(CTE)

  • 将查询分解成多个步骤,助于组织和简化复杂的查询逻辑

  • 支持递归查询,这对于处理层次结构数据非常有用

  • CTE如果被多次引用,它的结果只会被计算一次,可以提高性能

  • CTE如果只被使用一次,它的执行时间一般会长于同样逻辑的子查询

  • CTE在执行时通常会被物化,即创建临时表用于存储WITH语句的结果,导致额外的I/O和内存

  • 并非所有数据库系统都支持CTE,尤其是较老的版本。例如,MySQL直到8.0版本才支持CTE。

2.子查询

  • 子查询通常嵌套在主查询的FROM子句或WHERE子句中,这可能会使得查询难以阅读和维护,尤其是当子查询变得复杂时。

  • 不支持递归查询,在处理递归逻辑时不如CTE直观。

  • 子查询可能会被数据库优化器以不同的方式处理,有时它们可以被优化得更好,尤其是在子查询结果集较小的情况下

  • 子查询的结果通常不会被物化,而是在需要时动态计算,这可能会减少内存使用。

  • 几乎所有的数据库系统都支持子查询,因为它们是SQL标准的一部分。
### SQL 子查询的定义 子查询是指嵌套在一个外部查询中的内部查询。它可以在 `SELECT`、`INSERT`、`UPDATE` 或 `DELETE` 语句中使用,也可以作为表达式的组成部分。子查询的结果可以是一个单值、一列表或多行多列的数据集[^1]。 当子查询返回多行多列数据时,或者涉及更复杂的查询需求(如条件过滤无法通过简单的 `WHERE` 实现),可以通过将子查询放置在 `FROM` 子句中来实现复杂的需求。 --- ### SQL 子查询的主要使用场景 #### 场景 1: 复杂筛选条件 子查询常用于提供动态的筛选条件。例如,获取订单金额大于平均订单金额的所有客户: ```sql SELECT customer_id, order_amount FROM orders WHERE order_amount > (SELECT AVG(order_amount) FROM orders); ``` 上述例子展示了如何利用子查询计算出平均订单金额并将其应用于外层查询的条件判断中。 #### 场景 2: 数据关联分析 子查询可用于跨多个表进行数据分析。比如,找出销售额最高的产品类别及其对应的总销售金额: ```sql SELECT category_name, SUM(sales_amount) AS total_sales FROM products p JOIN ( SELECT product_id, SUM(amount) AS sales_amount FROM sales GROUP BY product_id ) s ON p.product_id = s.product_id GROUP BY category_name ORDER BY total_sales DESC; ``` 这里通过子查询先汇总每种产品的销售总额,再在外层查询中按类别分组统计。 #### 场景 3: 替代临时表功能 有时为了简化主查询逻辑,会把某些中间结果存放在子查询里代替创建物理临时表的操作。这不仅提高了效率还减少了额外资源消耗。 --- ### 结合视图优化查询过程 除了单独使用子查询之外,还可以考虑将频繁使用的复杂子查询封装成 **视图** 来提升可维护性和性能表现。例如,针对经常需要访问的商品分类体系,我们可以预先构建好相应的视图表以便后续快速调用[^2]: ```sql CREATE VIEW ProductHierarchyView AS WITH RECURSIVE CategoryTree AS ( -- 初始节点 SELECT id, parent_id, name FROM categories WHERE parent_id IS NULL UNION ALL -- 迭代展开层次结构 SELECT c.id, c.parent_id, c.name FROM categories c INNER JOIN CategoryTree ct ON c.parent_id = ct.id ) SELECT * FROM CategoryTree; -- 查询某一大类下的所有子类 SELECT * FROM ProductHierarchyView phv WHERE EXISTS( SELECT 1 FROM ProductHierarchyView root WHERE root.id = 'root_category_id' AND phv.id LIKE CONCAT(root.id, '%') ); ``` 此方法借助递归公用表表达式(CTE),能够有效解决树形结构数据遍历难题的同时保持良好的扩展能力[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值