简体   繁体   English

COALESCE SUM GROUP?

[英]COALESCE SUM GROUP?

Alright. 好的。 I have a query that looks like this: 我有一个查询,看起来像这样:

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`

The purpose of this query is to show the amount of items sold in a given date range. 该查询的目的是显示在给定日期范围内售出的商品数量。 Although this works, I now need it to show a count of 0 if a particular item has no sales in the date range. 虽然这个作品,我现在需要它来显示一个count0 ,如果一个特定的项目有日期范围内没有销售。 I tried using COALESCE around the SUM but that didn't do the trick, and I didn't really expect it to. 我尝试在SUM周围使用COALESCE ,但这并没有解决问题,而且我也没有真正期望它能做到。 Anyhow, does anyone know how I would go about accomplishing this? 无论如何,有人知道我将如何实现这一目标吗? I'm having one of those moments where I feel like I should know this but I can't think of it. 我正在那些时刻中的一刻,我觉得我应该知道这一点,但我想不起来。

Cheers 干杯

This can be done without any subqueries, if one puts the date conditions in the JOIN clause. 如果将日期条件放在JOIN子句中,则无需任何子查询即可完成此操作。

Below is code I tested on MySQL 5.0. 以下是我在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;

Output: 输出:

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

Here's the DDL and setup code, in the MySQL flavor: 这是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);

Randy's answer is close, but the where statement removes any mention of those items not part of any orders in that date range. Randy的答案很接近,但是where语句删除了该日期范围内不属于任何订单的那些物品的任何提及。

Note that "left join" is different to linking tables in the where clause in the manner you have done (ie inner joins). 请注意,“左联接”与您按照已完成的方式(即内部联接)在where子句中链接表不同。 I suggest you read up on the different types of SQL joins (inner, outer, cross). 我建议您阅读不同类型的SQL连接(内部,外部,交叉)。

In essense, you need to join the data you get from Randy's query against your source list of items. 本质上,您需要将从Randy的查询中获得的数据与项目的源列表相结合。 Using a subselect will do this: 使用子选择将执行以下操作:

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;

This is in Oracle 10g BTW. 这在Oracle 10g BTW中。 I doubt you're using Oracle, so you'll need to convert to your own database. 我怀疑您使用的是Oracle,因此您需要转换为自己的数据库。

Running a test shows the following: 运行测试将显示以下内容:

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: It's bad practice to use 'date' as a column name as it is (in most cases) a type name and can cause problems to queries and parses. PS:不好的做法是使用“日期”作为列名,因为它(在大多数情况下)是类型名,并可能导致查询和解析问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM