Sign in to run and submit your work
Reading is open to everyone. Running code and saving drafts need an account so your work is yours and comes back on your next visit.
or
CODE WORKSPACE
Support logs one row in shop_returns for every returned item. When a refund is given at the counter and nobody finds the original order, the row is still logged with order_id = NULL.
Return the orders that were never returned against, ordered by order_id.
Result columns · in this order
order_id | The order. |
customer_id | Who placed it. |
order_total | Its value. |
How to approach it
Write the anti-join as a question about existence rather than as a list membership test.
Sample input
| order_id | customer_id | placed_at | status | order_total |
|---|---|---|---|---|
| 1001 | c1 | 2026-01-05 10:00:00 | paid | 120 |
| 1002 | c2 | 2026-01-12 14:30:00 | Paid | 80 |
| 1003 | c1 | 2026-01-20 09:15:00 | pending | 45 |
| 1004 | c3 | 2026-01-31 23:30:00 | PAID | 200 |
| 1005 | c2 | 2026-02-01 00:15:00 | paid | 60 |
| 1006 | c4 | 2026-02-03 11:00:00 | paid | 150 |
| 1007 | c3 | 2026-02-10 16:45:00 | refunded | 90 |
| 1008 | c1 | 2026-02-14 12:00:00 | paid | 300 |
| 1009 | c5 | 2026-02-18 08:30:00 | Paid | 75 |
| 1010 | c4 | 2026-02-25 19:20:00 | pending | 85 |
| 1011 | c5 | 2026-02-28 21:00:00 | paid | 130 |
| 1012 | c2 | 2026-03-02 10:10:00 | paid | 95 |
12 rows — scroll inside the table to see them all.
| return_id | order_id | reason | refund_amount | returned_on |
|---|---|---|---|---|
| 1 | 1001 | damaged | 60 | 2026-01-09 |
| 2 | 1004 | wrong size | 50 | 2026-02-05 |
| 3 | 1004 | wrong size | 50 | 2026-02-20 |
| 4 | 1007 | changed mind | 90 | 2026-03-01 |
| 5 | null | unlinked | 25 | 2026-02-11 |
| 6 | 1008 | damaged | 150 | 2026-02-16 |
| 7 | 1011 | late delivery | 130 | 2026-03-20 |
| 8 | 1002 | damaged | 80 | 2026-01-15 |
| 9 | 1002 | damaged | 80 | 2026-01-18 |
| 10 | 1008 | damaged | 150 | 2026-02-20 |
10 rows — all rows shown.
Expected output
| order_id | customer_id | order_total |
|---|---|---|
| 1003 | c1 | 45 |
| 1005 | c2 | 60 |
| 1006 | c4 | 150 |
| 1009 | c5 | 75 |
| 1010 | c4 | 85 |
| 1012 | c2 | 95 |
6 rows — all rows shown.
Constraints
shop_returns points at it.customer_id and order_total alongside its id.Worked example
Order 1003 has no row anywhere in shop_returns, so it belongs in the answer alongside 1005, 1006, 1009, 1010 and 1012 — six orders in total.
The obvious query is WHERE order_id NOT IN (SELECT order_id FROM shop_returns), and on this data it returns no rows at all. Return 5 carries a NULL order_id, and NOT IN expands to a chain of <> comparisons in which comparing anything to NULL is unknown rather than true. One unknown is enough to stop the whole chain being true, so even order 1003 is rejected.
What this tests
Why NOT IN is unsafe against a nullable column, and that NOT EXISTS expresses the same anti-join without the three-valued-logic trap.
Submit for review to find out what your query gets right, what it gets wrong, and how it compares with the best working query for this exercise.
This scenario runs a full workspace — editor, canvas and results side by side. It needs a laptop or desktop to be usable. Open this page on a bigger screen to start building.