Archive Closed Orders

An online marketplace keeps its live orders small by moving old, finished orders into shop_orders_archive (same columns).

Move every order that was placed before 1 January 2026 (that is, any time up to 2025-12-31 23:59:59) and is closed, meaning its status is 'delivered' or 'cancelled'. Each moved order must be copied unchanged into shop_orders_archive and removed from shop_orders. Open orders ('pending', 'shipped') stay in shop_orders however old they are, and rows already in the archive stay there.

Do not return anything. The judge then lists both tables as tbl, order_id, buyer_id, placed_at, total_cents, status, where tbl is 'archive' or 'live', sorted by tbl, order_id.

Tables

shop_orders — Live orders, one row per order.

ColumnTypeDescription
order_id (key)INTEGERUnique order id
buyer_idINTEGERCustomer who placed the order
placed_atTEXTWhen the order was placed, 'YYYY-MM-DD HH:MM:SS'
total_centsINTEGEROrder total in cents
statusTEXT'pending', 'shipped', 'delivered' or 'cancelled'

shop_orders_archive — Archived orders, same layout as shop_orders. May already contain rows from earlier runs.

ColumnTypeDescription
order_id (key)INTEGERUnique order id
buyer_idINTEGERCustomer who placed the order
placed_atTEXTWhen the order was placed, 'YYYY-MM-DD HH:MM:SS'
total_centsINTEGEROrder total in cents
statusTEXT'delivered' or 'cancelled'

Example

shop_orders

order_idbuyer_idplaced_attotal_centsstatus
501122025-11-03 10:15:004599delivered
50272025-12-31 18:40:001250cancelled
503122025-12-20 09:05:008900shipped
50432026-01-01 00:00:002300delivered
50572026-01-14 13:30:001999pending
50632025-10-08 16:20:006400delivered

shop_orders_archive

order_idbuyer_idplaced_attotal_centsstatus
41052025-06-11 08:00:003100delivered

Expected output

tblorder_idbuyer_idplaced_attotal_centsstatus
archive41052025-06-11 08:00:003100delivered
archive501122025-11-03 10:15:004599delivered
archive50272025-12-31 18:40:001250cancelled
archive50632025-10-08 16:20:006400delivered
live503122025-12-20 09:05:008900shipped
live50432026-01-01 00:00:002300delivered
live50572026-01-14 13:30:001999pending

Return the rows in exactly the order described. Your query also runs against hidden datasets with tricky edge cases.

SQL Path · Changing Data · problem 61 of 70 · SQLite

Moving a row is two statements: copy it with INSERT ... SELECT, then remove it with DELETE.
placed_at is text with a time part. '2025-12-31 18:40:00' sorts after '2025-12-31', so think carefully about the boundary.
Both statements must select exactly the same rows. Either repeat the same WHERE clause or delete the ids that are now in the archive.

The idea

SQL has no single "move" command, so the job is a copy followed by a delete over the same set of rows.

Pick the rows once, precisely. The cut-off is the start of 2026, so the clean test is placed_at < '2026-01-01'. ISO timestamps compare correctly as text, and every moment of 31 December is smaller than '2026-01-01', while '2026-01-01 00:00:00' is not. Writing <= '2025-12-31' looks equivalent but fails for any order with a time later than midnight on that day. Add status IN ('delivered', 'cancelled') and wrap the OR in parentheses (or use IN) so it does not escape the date filter.

Copy, then delete. INSERT INTO shop_orders_archive SELECT ... copies the rows unchanged, then DELETE with the identical predicate removes them. The alternative deletes by id from the archive, which ties the delete directly to what was copied. In production you would run both inside one transaction so a failure cannot leave rows half-moved.

Solution

INSERT INTO shop_orders_archive (order_id, buyer_id, placed_at, total_cents, status)
SELECT order_id, buyer_id, placed_at, total_cents, status
FROM shop_orders
WHERE placed_at < '2026-01-01'
  AND status IN ('delivered', 'cancelled');

DELETE FROM shop_orders
WHERE placed_at < '2026-01-01'
  AND status IN ('delivered', 'cancelled');

Another way

INSERT INTO shop_orders_archive
SELECT *
FROM shop_orders
WHERE date(placed_at) <= '2025-12-31'
  AND status IN ('delivered', 'cancelled');

-- delete exactly the rows that now exist in the archive
DELETE FROM shop_orders
WHERE order_id IN (SELECT order_id FROM shop_orders_archive);

Why common answers fail

Compares a timestamp to a bare date with <=, so orders placed during 31 December ('2025-12-31 18:40:00' > '2025-12-31') are left behind.

INSERT INTO shop_orders_archive
SELECT * FROM shop_orders
WHERE placed_at <= '2025-12-31' AND status IN ('delivered', 'cancelled');
DELETE FROM shop_orders
WHERE placed_at <= '2025-12-31' AND status IN ('delivered', 'cancelled');

The DELETE drops the status filter, so old open orders vanish from the live table without ever reaching the archive.

INSERT INTO shop_orders_archive
SELECT * FROM shop_orders
WHERE placed_at < '2026-01-01' AND status IN ('delivered', 'cancelled');
DELETE FROM shop_orders
WHERE placed_at < '2026-01-01';

Copies the rows but never deletes them, so every archived order now exists twice.

INSERT INTO shop_orders_archive
SELECT * FROM shop_orders
WHERE placed_at < '2026-01-01' AND status IN ('delivered', 'cancelled');

AND binds tighter than OR, so every cancelled order is moved regardless of its date.

INSERT INTO shop_orders_archive
SELECT * FROM shop_orders
WHERE placed_at < '2026-01-01' AND status = 'delivered' OR status = 'cancelled';
DELETE FROM shop_orders
WHERE placed_at < '2026-01-01' AND status = 'delivered' OR status = 'cancelled';

What the interviewer is checking

Tests whether you keep the INSERT and DELETE predicates identical and handle timestamp boundaries correctly, which is where real archive jobs lose or duplicate data.

Mistakes to avoid

  • Using <= '2025-12-31' on a datetime column and missing orders placed later that day.
  • Different WHERE clauses in the INSERT and the DELETE, which loses or duplicates rows.
  • Mixing AND with OR without parentheses.
  • Forgetting the DELETE, leaving every archived order in both tables.

Follow-ups to expect

  • Make the move atomic (BEGIN; ...; COMMIT;).
  • Archive anything older than 90 days relative to today (placed_at < date('now', '-90 days')).
  • Archive in batches of 1,000 rows to avoid long locks (LIMIT on the selected ids and loop).

In real work

Data-retention jobs move closed orders, old logs or resolved tickets into cold tables every night to keep the hot tables small and fast.

Practice next

Next Post Previous Post
No Comment
Add Comment
comment url