COALESCE SUM GROUP? - PullRequest
       8

COALESCE SUM GROUP?

3 голосов
/ 05 ноября 2008

Хорошо. У меня есть запрос, который выглядит так:

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`

Цель этого запроса - показать количество товаров, проданных за указанный диапазон дат. Хотя это работает, мне нужно, чтобы оно показывало count из 0, если у определенного товара нет продаж в диапазоне дат. Я пытался использовать COALESCE вокруг SUM, но это не сработало, и я не ожидал этого. Во всяком случае, кто-нибудь знает, как я буду делать это? У меня один из тех моментов, когда я чувствую, что должен знать это, но я не могу думать об этом.

Приветствия

Ответы [ 2 ]

5 голосов
/ 05 ноября 2008

Это можно сделать без каких-либо подзапросов, если указать условие даты в предложении 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 | 
+--------+-------+

Вот код DDL и установочный код в варианте MySQL:

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);
3 голосов
/ 05 ноября 2008

Ответ Рэнди близок, но оператор where удаляет любое упоминание тех предметов, которые не входят ни в какие заказы в этом диапазоне дат.

Обратите внимание, что "левое соединение" отличается от связывания таблиц в предложении where тем же способом, что и вы (т.е. внутренние соединения). Я предлагаю вам ознакомиться с различными типами соединений 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_items.id
    ) counts on items.id = counts.id;

Это в Oracle 10g BTW. Я сомневаюсь, что вы используете 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: плохой практикой является использование «даты» в качестве имени столбца, так как оно (в большинстве случаев) является именем типа и может вызвать проблемы при запросах и синтаксическом анализе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...