A couple of queries tested and optimized. All return the same, all are basically standard SQL. (But no RDBMS supports the standard completely.)
The first one uses a LATERAL JOIN, which is missing in Oracle before 12c or MySQL. Test yourself which performs best.
All of them use index-only scans on the lookup table in Postgres. Obviously, lookup.nominal_value needs to be indexed. I suggest to make it UNIQUE because it seems like the column should be unique. That creates the all-important index automatically.
LATERAL JOIN
SELECT m.id, m.measurement, l.nominal_value
FROM measurement m
JOIN LATERAL (
(
SELECT nominal_value - m.measurement AS diff, nominal_value
FROM lookup
WHERE nominal_value >= m.measurement
ORDER BY nominal_value
LIMIT 1
)
UNION ALL
(
SELECT m.measurement - nominal_value, nominal_value
FROM lookup
WHERE nominal_value <= m.measurement
ORDER by nominal_value DESC
LIMIT 1
)
ORDER BY 1
LIMIT 1
) l ON true;
All parentheses required for UNION. See:
Correlated subqueries in a subquery
SELECT id, measurement
, CASE WHEN hi - measurement > measurement - lo
THEN lo
ELSE hi
END AS nominal_value
FROM (
SELECT id, measurement
, ( SELECT nominal_value
FROM lookup
WHERE nominal_value >= m.measurement
ORDER BY nominal_value
LIMIT 1) AS hi
, COALESCE((
SELECT nominal_value
FROM lookup
WHERE nominal_value <= m.measurement
ORDER by nominal_value DESC
LIMIT 1), 0) AS lo -- cover possible NULL values
FROM measurement m
) sub;
Correlated subqueries in a CTE
WITH cte AS (
SELECT id, measurement
, ( SELECT nominal_value
FROM lookup
WHERE nominal_value >= m.measurement
ORDER BY nominal_value
LIMIT 1) AS hi
, COALESCE((
SELECT nominal_value
FROM lookup
WHERE nominal_value <= m.measurement
ORDER by nominal_value DESC
LIMIT 1), 0) AS lo -- cover possible NULL values
FROM measurement m
)
SELECT id, measurement
, CASE WHEN hi - measurement > measurement - lo
THEN lo
ELSE hi
END AS nominal_value
FROM cte;
Nested correlated subqueries
SELECT id, measurement
, (SELECT nominal_value FROM (
(
SELECT nominal_value - m.measurement, nominal_value
FROM lookup
WHERE nominal_value >= m.measurement
ORDER BY nominal_value
LIMIT 1
)
UNION ALL
(
SELECT m.measurement - nominal_value, nominal_value
FROM lookup
WHERE nominal_value <= m.measurement
ORDER by nominal_value DESC
LIMIT 1
)
ORDER BY 1
LIMIT 1
) sub
) AS nominal_value
FROM measurement m;
db<>fiddle here
Old sqlfiddle