0

I have the following tables:

CREATE TABLE `orders` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` int(10) unsigned NOT NULL,
  `billing_profile_id` int(10) unsigned NOT NULL,
  `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `currency` varchar(3) COLLATE utf8_unicode_ci NOT NULL,
  `valid_until` timestamp NULL DEFAULT NULL
)

And:

CREATE TABLE `items` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `order_id` int(10) unsigned NOT NULL,
  `status` enum('pending','processing','completed','on-hold','cancelled'),
  `code` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `quantity` int(10) unsigned NOT NULL DEFAULT '1',
  `term` int(10) unsigned NOT NULL,
  `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `discounts` int(10) unsigned NOT NULL,
  `total` int(10) unsigned NOT NULL
)

How do I SELECT only the orders that have all of its items with status completed?

I tried with this:

SELECT o.* FROM 
FROM orders o INNER JOIN items i on o.id=i.order_id
GROUP BY o.id, i.status
HAVING i.status = 'completed'

But that returns only the orders that have some items completed, not all.

EDIT: An order with no items is also a valid result.

Cabezota
  • 107
  • 9

3 Answers3

4

Try this:

SELECT *
FROM   orders
WHERE  EXISTS (SELECT *
               FROM   items
               WHERE  orders.id = items.order_id
                 AND  items.status = 'completed')
  AND  NOT EXISTS (SELECT *
                   FROM   items
                   WHERE  orders.id = items.order_id
                     AND  items.status <> 'completed')
Robert Kock
  • 5,795
  • 1
  • 12
  • 20
1

Easy:

select * from orders
  where id not in (
    select order_id from items where status <> 'completed'
  )
The Impaler
  • 45,731
  • 9
  • 39
  • 76
1
DROP TABLE IF EXISTS orders;
CREATE TABLE orders (
  `id` SERIAL PRIMARY KEY
  );

INSERT INTO orders VALUES
(100),(101);

DROP TABLE IF EXISTS items;

CREATE TABLE `items` (
  `id` SERIAL PRIMARY KEY,
  `order_id` int(10) unsigned NOT NULL,
  `status` enum('pending','processing','completed','on-hold','cancelled')
);

INSERT INTO items VALUES
(1,100,'completed'),
(2,100,'completed'),
(3,100,'completed'),
(4,101,'completed'),
(5,101,'processing');


SELECT DISTINCT o.* 
           FROM orders o
           LEFT 
           JOIN items i 
             ON i.order_id = o.id 
            AND i.status <> 'completed' 
          WHERE i.id IS NULL;

+-----+
| id  |
+-----+
| 100 |
+-----+

...or...

SELECT DISTINCT o.* 
           FROM orders o
          WHERE NOT EXISTS (SELECT 1 
                              FROM items i 
                             WHERE i.order_id = o.id 
                               AND i.status <> 'completed');
Strawberry
  • 33,750
  • 13
  • 40
  • 57
  • This answer seems to have equivalent cost to the previous ones posted already. If you think is cheaper, can you post the execution plan for it? – The Impaler Mar 19 '18 at 14:14
  • Just checked it, and it runs a full table scan on both tables. It's not faster. Here's the plan: id select_type table type Extra -- ----------- ----- ---- ----- 1 SIMPLE o ALL Using temporary 1 SIMPLE i ALL Using where; Not exists; Distinct – The Impaler Mar 19 '18 at 14:21