Call function where argument is a (sub)select statement
-
20-12-2020 - |
Question
My function takes an int4
as argument and returns a table:
SELECT * FROM test_function(545421); -- works fine
SELECT * FROM test_function(SELECT customerid
FROM tableX where id = 1); -- syntax error
How can I make this work?
Server PostgreSQL 9.3.1
Solution
1. Subquery expression
You can fix it with parentheses like @a_horse commented:
SELECT * FROM test_function((SELECT customerid FROM tableX where id = 1));
But this form is rather error-prone. Nothing in the code guarantees that the sub-select only returns a single row. We don't know whether id
is unique and neither does Postgres (unless it looks up system catalogs). We have to rely on it and hope it won't break.
We could add LIMIT 1
to be sure, but then we probably should also add ORDER BY
to get deterministic results ...
2. JOIN LATERAL
The modern syntax for this (Postgres 9.3+) would be a LATERAL
join:
SELECT f.*
FROM tableX t, test_function(t.customerid) f
WHERE t.id = 1;
Which is short syntax for:
SELECT f.*
FROM tableX t
CROSS JOIN LATERAL test_function(t.customerid) f
WHERE t.id = 1;
Exactly the same result as above, except for one subtle but possibly important difference:
-
- If
tableX
has no row fort.id = 1
, the function is never called and the query returns nothing (no row). If, on the other hand, a row is found withcustomerid IS NULL
, the function is called withNULL
input and we get whatever the function returns forNULL
.
That's how it should be.
- If
-
- The form with the subquery expression cannot distinguish between these two cases. "No row" results in the same NULL value as
customerid
beingNULL
. The function is called withNULL
input either way.
That's not how it should be.
- The form with the subquery expression cannot distinguish between these two cases. "No row" results in the same NULL value as
The LATERAL
query also does not break if id
is not unique. And you can easily return additional columns from tableX
if you want.