【问题标题】:COALESCE SUM GROUP?合并总和集团?
【发布时间】:2010-09-20 19:11:09
【问题描述】:

好的。我有一个看起来像这样的查询:

SELECT
    SUM(`order_items`.`quantity`) as `count`,
    `menu_items`.`name`
FROM 
    `orders`,
    `menu_items`,
    `order_items` 
WHERE 
    `orders`.`id` = `order_items`.`order_id` AND 
    `menu_items`.`id` = `order_items`.`menu_item_id` AND 
    `orders`.`date` >= '2008-11-01' AND 
    `orders`.`date` <= '2008-11-30' 
GROUP BY 
    `menu_items`.`id`

此查询的目的是显示在给定日期范围内售出的商品数量。尽管这可行,但如果特定商品在日期范围内没有销售,我现在需要它显示 0count。我尝试在SUM 周围使用COALESCE,但这并没有成功,而且我真的没想到会这样。无论如何,有谁知道我将如何实现这一目标?我有一个时刻,我觉得我应该知道这一点,但我想不起来。

干杯

【问题讨论】:

    标签: sql mysql


    【解决方案1】:

    如果将日期条件放在JOIN 子句中,则无需任何子查询即可完成此操作。

    以下是我在 MySQL 5.0 上测试的代码。

    SELECT m.name, COALESCE(SUM(oi.quantity), 0) AS count
    FROM menu_items AS m
      LEFT OUTER JOIN (
        order_items AS oi JOIN orders AS o
          ON (o.id = oi.order_id)
      ) ON (m.id = oi.menu_item_id
          AND o.`date` BETWEEN '2008-11-01' AND '2008-11-30')
    GROUP BY m.id;
    

    输出:

    +--------+-------+
    | name   | count |
    +--------+-------+
    | bread  |     2 | 
    | milk   |     1 | 
    | honey  |     2 | 
    | cheese |     0 | 
    +--------+-------+
    

    这是 MySQL 风格的 DDL 和设置代码:

    DROP TABLE IF EXISTS menu_items;
    CREATE TABLE menu_items (
      id            INT PRIMARY KEY,
      name          VARCHAR(10)
    ) TYPE=InnoDB;
    
    DROP TABLE IF EXISTS orders;
    CREATE TABLE orders (
      id            INT PRIMARY KEY,
      `date`        DATE
    ) TYPE=InnoDB;
    
    DROP TABLE IF EXISTS order_items;
    CREATE TABLE order_items (
      order_id      INT,
      menu_item_id  INT,
      quantity      INT,
      PRIMARY KEY (order_id, menu_item_id),
      FOREIGN KEY (order_id) REFERENCES orders(id),
      FOREIGN KEY (menu_item_id) REFERENCES menu_items(id)
    ) TYPE=InnoDB;
    
    INSERT INTO menu_items VALUES
      (1, 'bread'),
      (2, 'milk'),
      (3, 'honey'),
      (4, 'cheese');
    
    INSERT INTO orders VALUES
      (1, '2008-11-02'),
      (2, '2008-11-03'),
      (3, '2008-10-29');
    
    INSERT INTO order_items VALUES
      (1, 1, 1),
      (1, 3, 1),
      (2, 1, 1),
      (2, 2, 1),
      (2, 3, 1),
      (3, 4, 10);
    

    【讨论】:

      【解决方案2】:

      Randy 的回答很接近,但 where 语句删除了对那些不属于该日期范围内任何订单的项目的任何提及。

      请注意,“左连接”不同于以您所做的方式在 where 子句中链接表(即内连接)。我建议您阅读不同类型的 SQL 连接(内连接、外连接、交叉连接)。

      本质上,您需要将您从 Randy 的查询中获得的数据与您的项目源列表结合起来。使用子选择会这样做:

      SELECT
          name
          , nvl(count, 0) as count
      FROM 
          menu_items items 
          LEFT JOIN (
              SELECT
                  menu_items.id
                  , SUM(order_items.quantity) as count
              FROM 
                  menu_items
                  LEFT JOIN order_items ON menu_items.id = order_items.menu_item_id
                  LEFT JOIN orders ON orders.id = order_items.order_id
              WHERE
                  "date" between to_date('2008-11-01','YYYY-MM-DD') and to_date('2008-11-30','YYYY-MM-DD')
              GROUP BY
                  menu_items.id
          ) counts on items.id = counts.id;
      

      顺便说一句,这是在 Oracle 10g 中。我怀疑您使用的是 Oracle,因此您需要转换为您自己的数据库。

      运行测试显示如下:

      SQL> create table menu_items ( id number, name varchar2(10));
      create table order_items (order_id number, menu_item_id number, quantity number);
      create table orders (id number, "date" date);
      
      Table created.
      
      SQL> 
      Table created.
      
      SQL> 
      Table created.
      
      SQL> 
      insert into menu_items values (1, 'bread');
      insert into menu_items values (2, 'milk');
      insert into menu_items values (3, 'honey');
      insert into menu_items values (4, 'cheese');
      SQL> 
      1 row created.
      
      SQL> 
      1 row created.
      
      SQL> 
      1 row created.
      
      SQL> 
      1 row created.
      
      SQL> 
      insert into orders values (1, to_date('2008-11-02', 'YYYY-MM-DD'));
      insert into orders values (2, to_date('2008-11-03', 'YYYY-MM-DD'));
      insert into orders values (3, to_date('2008-10-29', 'YYYY-MM-DD'));SQL> 
      1 row created.
      
      SQL> 
      1 row created.
      
      SQL> 
      insert into order_items values (1, 1, 1);
      insert into order_items values (1, 3, 1);
      1 row created.
      
      SQL> 
      1 row created.
      
      SQL> 
      insert into order_items values (2, 1, 1);
      insert into order_items values (2, 2, 1);
      insert into order_items values (2, 3, 1);
      
      insert into order_items values (3, 4, 10);
      1 row created.
      
      SQL> 
      1 row created.
      
      SQL> 
      1 row created.
      
      SQL> 
      1 row created.
      
      SQL> SQL> 
      
      1 row created.
      
      SQL> 
      SELECT
          name
          , nvl(count, 0) as count
      FROM 
          menu_items items 
          LEFT JOIN (
              SELECT
                  menu_items.id
                  , SUM(order_items.quantity) as count
              FROM 
                  menu_items
                  LEFT JOIN order_items ON menu_items.id = order_items.menu_item_id
                  LEFT JOIN orders ON orders.id = order_items.order_id
              WHERE
                  "date" between to_date('2008-11-01','YYYY-MM-DD') and to_date('2008-11-30','YYYY-MM-DD')
              GROUP BY
                  menu_iteSQL>   2    3    4    5    6    7  ms.id
          ) counts on items.id = counts.id;  8    9   10   11   12   13   14   15   16   17   18  
      
      NAME            COUNT
      ---------- ----------
      bread               2
      milk                1
      honey               2
      cheese              0
      
      SQL> 
      drop table menu_items;
      drop table order_items;
      drop table orders;SQL> 
      Table dropped.
      
      SQL> 
      Table dropped.
      
      SQL> 
      
      Table dropped.
      
      SQL> 
      

      PS:使用“日期”作为列名是不好的做法,因为它(在大多数情况下)是类型名,可能会导致查询和解析出现问题。

      【讨论】:

      • 很好,杰米。子查询比我的少 1 个,而且更合理。 MySQL 唯一需要改变的就是去掉 Oracle 的日期函数。 +1
      • 我的要求显然比我发布的查询要复杂一点,但我用它来构建它,现在它工作得很好。非常感谢。
      猜你喜欢
      • 2020-03-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-07
      • 2017-06-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多