在连接中使用 Where 子句,以及 Group by 和 Order By

Posted

技术标签:

【中文标题】在连接中使用 Where 子句,以及 Group by 和 Order By【英文标题】:Using Where clause in a join, with Group by and Order By 【发布时间】:2012-05-12 02:16:20 【问题描述】:

我正在使用 mysql 经典模型数据库。以下查询工作正常(注意 where 子句)

select 
    customers.customerNumber as 'Customer ID',
    customers.customerName as 'Customer Name',
    count(orders.orderNumber) as 'Total Orders Placed'
from customers
left join orders on customers.customerNumber = orders.customerNumber
where customers.customerNumber > 200
group by
    customers.customerNumber
order by 
    3 asc

但以下导致错误。目的是仅显示结果行集中那些下达 3 个以上订单的客户。我做错了什么?

select 
    customers.customerNumber as 'Customer ID',
    customers.customerName as 'Customer Name',
    count(orders.orderNumber) as 'Total Orders Placed'
from customers
left join orders on customers.customerNumber = orders.customerNumber
where count(orders.orderNumber) > 3
group by
    customers.customerNumber
order by 
    3 asc

MySQL 错误为:错误代码:1111. 组函数使用无效

【问题讨论】:

【参考方案1】:

聚合函数(COUNT(), AVG(), SUM(), 等)不能出现在 WHERE 子句中,因为它们的计算时间。相反,它们属于HAVING 子句:

select 
    customers.customerNumber as 'Customer ID',
    customers.customerName as 'Customer Name',
    count(orders.orderNumber) as 'Total Orders Placed'
from customers
left join orders on customers.customerNumber = orders.customerNumber
group by
    customers.customerNumber
HAVING count(orders.orderNumber) > 3
order by 
    3 asc

【讨论】:

而且由于我们希望客户有超过 3 个订单,因此将 left join 转为 inner join 没有任何害处【参考方案2】:

更有效的方法是通过聚合将组移动到嵌套派生表中并加入该表。

SELECT 
  c.customerNumber AS 'Customer ID',
  c.customerName AS 'Customer Name',
  o.orderCount AS 'Total Orders Placed'
FROM 
  customers c
LEFT JOIN
  (SELECT
     customerNumber, 
     COUNT(*) AS orderCount
   FROM
     orders
   GROUP BY
     customerNumber) o
 ON
   c.customerNumber = o.customerNumber
 WHERE
   o.orderCount > 3
 ORDER BY
   3

【讨论】:

效率更高或更低(或者它是否无关紧要,优化器会在内部找出一个查询并将其转换为另一个)取决于优化器。【参考方案3】:

在where子句中有一种间接使用聚合函数的方法

您可以将查询重写为


select customers.customerNumber as 'Customer ID',
       customers.customerName as 'Customer Name',
       count(orders.orderNumber) as 'Total Orders Placed'
  from customers left join orders on customers.customerNumber =
                                     orders.customerNumber
 where
 (SELECT CASE
        WHEN count(orders.orderNumber) > 3 THEN
             'TRUE'
        ELSE
             'FALSE'
        END
   FROM DUAL) = 'TRUE'
 group by customers.customerNumber
 order by 3 asc

这里在 select 子句中使用 count 函数,根据 count 计算返回 FALSE 字符串的 TRUE。

希望对你有帮助

【讨论】:

【参考方案4】:

请试试这个

select 
  customers.customerNumber as 'Customer ID',
  customers.customerName as 'Customer Name',
  count(orders.orderNumber) as 'Total Orders Placed'
from customers
inner join orders on customers.customerNumber = orders.customerNumber
group by
  customers.customerNumber, 
  customers.customerName
having count(orders.orderNumber) > 3
order by 
  3 asc

【讨论】:

【参考方案5】:

这是您要实现的目标的简化版本。

SELECT 
    customers.customerName,
    COUNT(orders.orderNumber) totalOrders
FROM
    customers
        JOIN
    orders ON customers.customerNumber = orders.customerNumber
GROUP BY customers.customerName
HAVING totalOrders > 2;

【讨论】:

以上是关于在连接中使用 Where 子句,以及 Group by 和 Order By的主要内容,如果未能解决你的问题,请参考以下文章

oracle group by 性能优化

WHERE 子句 IN group_concat

在 WHERE 或 GROUP BY 子句中使用列表别名

不能在 Group by/Order by/Where/ON 子句中使用 Group 或 Aggregate 函数(min()、max()、sum()、count()、...等)

在 mysql WHERE 子句中使用 GROUP_CONCAT 和 FIND_IN_SET

在 from 子句 *and* where 子句中添加连接条件使查询更快。为啥?