Since MySQL 8.0.19 524 , VALUES is a statement of its own, a table value constructor whose rows are written ROW(...). Alone, its columns are named column_0, column_1 and so on; as a derived table, you name them in a column list, giving you a lookup table without creating one:
SELECT s.status, s.label, COUNT(o.id) AS orders
FROM (VALUES ROW('paid', 'Paid'), ROW('shipped', 'On its way'),
ROW('refunded', 'Refunded')) AS s (status, label)
LEFT JOIN orders AS o ON o.status = s.status
GROUP BY s.status, s.label;
VALUES ROW(1, 2), ROW(3);Output
+----------+------------+--------+ | status | label | orders | +----------+------------+--------+ | paid | Paid | 3 | | shipped | On its way | 4 | | refunded | Refunded | 0 | +----------+------------+--------+ 3 rows in set (0.001 sec) ERROR 1136 (21S01): Column count doesn't match value count at row 2
refunded shows a zero because the constructed table drives the join. Likewise, (VALUES ROW(1), ROW(5), ROW(42)) AS v (id) LEFT JOIN products with WHERE p.id IS NULL finds the pasted id 42 that has no product. Rows need equal lengths, and ROW is compulsory: bare VALUES (1, 2) is a syntax error. The sibling TABLE categories abbreviates SELECT * FROM categories and accepts ORDER BY and LIMIT.