目录
SQL中的WITH
语句,也称为公用表表达式(Common Table Expressions,简称CTE),允许你定义一个临时的结果集,这个结果集可以在一个或多个SELECT
、INSERT
、UPDATE
或DELETE
语句中被引用。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标准的一部分。