【问题标题】:How to select rows with WHERE statement and COUNT(*) = 1如何使用 WHERE 语句和 COUNT(*) = 1 选择行
【发布时间】:2019-02-17 00:02:35
【问题描述】:

我正在尝试将 WHERE 子句与 HAVING COUNT(*) 子句结合起来。但我无法得到我想要的结果。

我想查找所有只下过一个订单且订单为"inactive" 的客户。换句话说,我正在尝试从 customer_name 值的计数为“1”且订单状态等于 "inactive" 的订单表中选择行。

我有这张桌子:

orders_tbl
+----+--------------+-------------+---------+-------+
| ID |    cust_name |      status | item_no | price |
+----+--------------+-------------+---------+-------+
|  1 |        Scott |      active |       4 |   2.0 |
+----+--------------+-------------+---------+-------+
|  2 |        James |      active |       2 |   4.0 |
+----+--------------+-------------+---------+-------+
|  3 |         Eric |    inactive |       3 |   8.0 |
+----+--------------+-------------+---------+-------+
|  4 |        Polly |      active |       3 |   2.0 |
+----+--------------+-------------+---------+-------+
|  5 |        Peggy |    inactive |       6 |   4.0 |
+----+--------------+-------------+---------+-------+
|  6 |         Earl |    inactive |       1 |   5.0 |
+----+--------------+-------------+---------+-------+
|  7 |        Billy |      active |       4 |   2.0 |
+----+--------------+-------------+---------+-------+
|  8 |        Peggy |    inactive |       5 |   4.0 |
+----+--------------+-------------+---------+-------+
|  9 |        Jenny |    inactive |       4 |   8.0 |
+----+--------------+-------------+---------+-------+
| 10 |        Polly |      active |       2 |   2.0 |
+----+--------------+-------------+---------+-------+
| 11 |        Scott |    inactive |       2 |   4.0 |
+----+--------------+-------------+---------+-------+
| 12 |        James |    inactive |       1 |   8.0 |
+----+--------------+-------------+---------+-------+

我想要只有 一个 订单的每个客户的姓名,并且该订单是“非活动”的。从上表中,我想要这些结果:

+----+--------------+-------------+---------+-------+
| ID | cust_name    |     status  | item_no | price |
+----+--------------+-------------+---------+-------+
|  3 |         Eric |    inactive |       3 |   8.0 |
+----+--------------+-------------+---------+-------+
|  6 |         Earl |    inactive |       1 |   5.0 |
+----+--------------+-------------+---------+-------+
|  9 |        Jenny |    inactive |       4 |   8.0 |
+----+--------------+-------------+---------+-------+

我尝试了这个查询,以及使用 WHERE 和 COUNT() 的许多变体:

SELECT ID, cust_name, status, item_no, price, COUNT(*) 
FROM orders_tbl
WHERE status = 'inactive'
GROUP BY cust_name
HAVING COUNT(*)<2;

上述查询产生的结果接近我想要的结果。但是我得到的客户有一个"inactive" 记录,即使他们有一个或多个活动记录。有我得到的结果:

orders_tbl
+----+--------------+-------------+---------+-------+
| ID |    cust_name |      status | item_no | price |
+----+--------------+-------------+---------+-------+
|  3 |         Eric |    inactive |       3 |   8.0 |
+----+--------------+-------------+---------+-------+
|  5 |        Peggy |    inactive |       6 |   4.0 |
+----+--------------+-------------+---------+-------+
|  6 |         Earl |    inactive |       1 |   5.0 |
+----+--------------+-------------+---------+-------+
|  9 |        Jenny |    inactive |       4 |   8.0 |
+----+--------------+-------------+---------+-------+
| 11 |        Scott |    inactive |       2 |   4.0 |
+----+--------------+-------------+---------+-------+
| 12 |        James |    inactive |       1 |   8.0 |
+----+--------------+-------------+---------+-------+

【问题讨论】:

    标签: mysql sql group-by count where-clause


    【解决方案1】:

    一种解决方案是使用聚合,并将逻辑移动到HAVING 子句。以下查询可以为您提供满足条件的客户名称::

    SELECT cust_name
    FROM mytable
    GROUP BY cust_name
    HAVING COUNT(*) = 1 AND MAX(status) = 'inactive'
    

    返回:

    | cust_name |
    | --------- |
    | Earl      |
    | Eric      |
    | Jenny     |
    

    如果您还想查看订单,则可以将其转换为子查询:

    SELECT *
    FROM mytable
    WHERE cust_name IN (
        SELECT cust_name
        FROM mytable
        GROUP BY cust_name
        HAVING COUNT(*) = 1 AND MAX(status) = 'inactive'
    )
    

    结果:

    | ID  | cust_name | status   | item_no | price |
    | --- | --------- | -------- | ------- | ----- |
    | 3   | Eric      | inactive | 3       | 8     |
    | 6   | Earl      | inactive | 1       | 5     |
    | 9   | Jenny     | inactive | 4       | 8     |
    

    Demo on DB Fiddle


    从 MySQL 8.0 开始,窗口函数使它变得更容易、更高效。您可以将它们内联以按客户计算订单总数以及非活动订单的数量;然后在两个值都为 1 的记录中拟合:

    SELECT * FROM (
        SELECT 
            t.*, 
            COUNT(*) OVER(PARTITION BY cust_name) cnt_total, 
            SUM(status = 'inactive') OVER(PARTITION BY cust_name) cnt_inactive
        FROM mytable t
     ) x WHERE cnt_total = 1 AND cnt_inactive = 1;
    

    Demo on DB Fiddle

    【讨论】:

    • 感谢专线小巴。我尝试了所有三个选择语句。前两次在我的数据库上超时。我可能应该提到我的表有大约 900 万行。有什么方法可以将选择分解为不会超时的较小步骤?第三条语句返回错误码:1064。You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '(PARTITION BY cust_name) cnt_total, SUM(status = 'inactive') OVER(PARTI' at line 4
    猜你喜欢
    • 2011-06-17
    • 1970-01-01
    • 2010-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-22
    • 1970-01-01
    • 2014-03-14
    相关资源
    最近更新 更多