2008-11-04 7 views
3

Très bien. J'ai une question qui ressemble à ceci:COALESCE SUM GROUP?

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` 

Le but de cette requête est de montrer la quantité d'articles vendus dans une période donnée. Bien que cela fonctionne, je dois maintenant afficher un count de 0 si un article particulier n'a pas de ventes dans la plage de dates. J'ai essayé d'utiliser COALESCE autour du SUM mais cela n'a pas fait l'affaire, et je ne m'y attendais pas vraiment. De toute façon, est-ce que quelqu'un sait comment j'irais accomplir cela? J'ai un de ces moments où je sens que je devrais le savoir mais je ne peux pas y penser.

Vive

Répondre

5

Cela peut être fait sans aucune sous-requête, si l'on met les conditions de date dans la clause JOIN.

Voici le code que j'ai testé sur 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; 

Sortie:

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

Voici le code DDL et la configuration, la saveur 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

La réponse de Randy est proche, mais la déclaration où supprime toute mention de ces éléments ne font pas partie de toutes les commandes dans cette plage de dates. Notez que «jointure à gauche» est différent des tables de liaison dans la clause where de la manière que vous avez effectuée (c'est-à-dire, jointures internes). Je vous suggère de lire sur les différents types de jointures SQL (interne, externe, croix).

En substance, vous devez joindre les données que vous obtenez de la requête de Randy par rapport à votre liste d'éléments source. L'utilisation d'une sous-sélection permet de faire ceci:

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; 

Ceci est dans Oracle 10g BTW. Je doute que vous utilisiez Oracle, vous devrez donc le convertir dans votre propre base de données.

Exécution d'un test montre les éléments suivants:

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: Il est une mauvaise pratique d'utiliser « date » comme nom de colonne comme il est (dans la plupart des cas) un nom de type et peut causer des problèmes aux requêtes et analyse

+0

Bonne affaire, Jamie. 1 sous-requête en moins que la mienne, et beaucoup plus raisonnable. La seule chose qui aura besoin de changer pour MySQL sera de supprimer les fonctions de la fonction de date d'Oracle. +1 – Randy

+0

Mes exigences étaient évidemment un peu plus complexes que la requête que j'ai posté, mais j'ai utilisé cela pour continuer et ça fonctionne parfaitement maintenant. Merci beaucoup. –