【MySQL必知必会】:使用子查询

140 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 24 天,点击查看活动详情

子查询

MySQL 4.1 引入了对子查询的支持

前面我们记录过的 select 语句都是从单个数据库表中检索数据的单条语句。SQL 还允许嵌套在其他查询中的查询,即子查询(subquery)。


利用子查询进行过滤

如果一条 select 语句的返回结果可以被用于另一条 select 语句的 where 子句中,那么我们可以将这两条查询语句组合成一条语句,这就利用到了子查询

我们知道,在MySQL中使用的数据库表都是关系表。

假设,现在有三张表:

  • orders 表:存储订单号,客户ID,订单日期
  • orderitems 表:存储各订单的物品
  • customers 表:存储实际的客户信息

然后,需求是列出订购物品 abc 的所有客户,此时应该怎样检索?步骤如下:

  1. 检索包含物品 abc 的所有订单的编号;

    输入:

    select
        order_id
    from
        orderitems
    where
        prod_id = 'abc';
    

    输出:

    +----------+
    | order_id |
    +----------+
    |    10001 |
    |    10007 |
    +----------+
    
  2. 检索步骤一列出的订单编号的所有客户ID;

    输入:

    select
        cust_id
    from
        orders
    where
        order_id in (10001, 10007);
    

    输出:

    +---------+
    | cust_id |
    +---------+
    |    2005 |
    |    2009 |
    +---------+
    
  3. 最后是返回步骤二检索出的客户ID的客户信息。

    输入:

    select
        cust_name,
        cust_contact
    from
        customers
    where
        cust_id in (2005, 2009);
    

现在,我们可以将步骤一和步骤二变为子查询,即组合两个查询,记录如下:

select
    cust_id
from
    orders
where
    order_id in (
        select
            order_id
        from
            orderitems
        where
            prod_id = 'abc';
    );

此时的输出结果为:

+---------+
| cust_id |
+---------+
|    2005 |
|    2009 |
+---------+

记录:

  • select 语句中,子查询总是从内向外进行处理。

  • 在上述子查询的例子中,MySQL 实际上会先执行下面的查询:

    select order_id from orderitems where prod_id = 'abc';
    

    该查询返回了两个订单号:1000110007。然后,这两个值以 IN 操作符要求的逗号分隔的格式传递给外部查询的 where 子句。此时的外部查询变为:

    select cust_id from	orders where order_id in (10001, 10007);
    

当然,我们也可以利用子查询将三条语句组合成一条。可见,在 where 子句中利用子查询能够编写出灵活强大的 SQL 语句。

注意,由于性能的限制,不宜过多嵌套子查询。

作为计算字段使用子查询

使用子查询的另一个方法就是创建计算字段。

假设,我们现在有两张表:

  • customers表:包含客户信息以及订单信息
  • orders表:包含订单以及相应的客户ID

现在我们需要返回每个客户的订单总数,步骤如下:

  1. customers 表中检索出客户ID列表
  2. 根据客户ID,统计其在 orders 表中的订单数目

在之前,我们对某个客户(比如 1001)的订单进行计数,可以是这样:

select
	count(*) as orders
from
	orders
where
	cust_id = 1001;

现在,我们需要返回每个客户的订单总数,此时应该将 count(*) 作为一个子查询。

如下所示:

select
	cust_name,
	(
    	select count(*)
        from orders
        where orders.cust_id = customers.cust_id
    ) as orders
from
	customers
order by
	cust_name;

记录:

  • orders 是一个计算字段,它由圆括号中的子查询建立的。
  • 该子查询对检索出的每个客户执行一次。

备注:

  • where order.cust_id = customers.cust_id
  • 上述语句使用了完全限定列名,其用于告诉 SQL 比较 orders 表中的 cust_id 与当前正在从 customers 表中检索的 cust_id