Values Scan
Appears in EXPLAIN asValues Scan
Reads rows from an inline VALUES list.
What it is
A Values Scan streams the rows of a literal VALUES (...) , (...) construct — for example a multi-row INSERT or a VALUES list joined in FROM.
When the planner picks it
Whenever a VALUES list appears in the query.
Is it good or bad?
Trivially cheap. A handy trick: joining against a VALUES list is an efficient way to pass a small set of constants into a query.
In depth
A list of constants, planned
A Values Scan streams the rows of a literal VALUES list — whether it's a
multi-row INSERT, a VALUES list in FROM, or the constant side of a
generated join.
INSERT INTO points (x, y) VALUES (1, 2), (3, 4), (5, 6);
SELECT *
FROM (VALUES ('a', 1), ('b', 2), ('c', 3)) AS t(label, n);
The rows are stored inline in the plan and have known cardinality, so the planner's estimate is exact — no surprise row-count problems here.
A useful trick
Joining against a small VALUES list is an efficient way to pass a set of
keys into a query without the overhead of a temp table or a long IN list:
SELECT u.*, v.note
FROM users u
JOIN (VALUES (1, 'first'), (4, 'second'), (7, 'third')) AS v(id, note)
USING (id);
The planner treats it as a tiny relation; for outer-side joins this often produces an efficient nested loop with an indexed inner lookup.
Limits
The cost is essentially zero, but very long VALUES lists (thousands of
rows) bloat plan size and increase planning time. For bulk inserts, prefer
COPY or a single multi-row INSERT from a SELECT … FROM unnest(array) —
both keep the plan compact.