I have a function like this, which always returns exactly one record:
CREATE FUNCTION foo(pid int) RETURNS TABLE (a int, b int)
AS $$
-- …
$$ LANGUAGE sql;
I want to select rows from a table like this:
SELECT p.id
, foo(p.id).*
FROM puns AS p;
However, this gives a syntax error at .*. I couldn’t figure out how to do it with a JOIN, since p.id is an argument to the function.
How can I unpack the record returned by the function into the select query? The desired result would have three columns id, a and b.
I’m using PostgreSQL 9.2.
Option 1 - upgrade to postgres 9.3 and use LATERAL.
Option 2 - use something like:
SELECT sub_q.id, (sub_q.foo_row).a, (sub_q.foo_row).b
FROM (
SELECT p.id
, foo(p.id) as foo_row
FROM puns AS p ) sub_q;
Option 3 - try additional brackets like:
SELECT p.id
, (foo(p.id)).*
FROM puns AS p;
But I haven't tested option 3.
Related
I'm trying to wrap a recursive temporary table query inside a function on MariaDB 10.3.7 on Ubuntu 18.04. I've tried breaking the statement down to the more basic parts but it all works correctly until I put it all together.
This is an example of the statement I want to turn into a function:
with recursive Descendants as (
select * from Characters where id = 91402
union
select c.* from Characters as c, Descendants as d
where d.id = c.mother_id or d.id = c.real_father_id
) select count(distinct(id)) from Descendants
It print out the number of descendant characters, which is what I want.
And here is my attempt so far at turning it into a function:
create function count_descendants(cid int unsigned) returns int unsigned return (
with recursive Descendants as (
select * from Characters where id = cid
union
select c.* from Characters as c, Descendants as d
where d.id = c.mother_id or d.id = c.real_father_id
) select count(distinct(id)) from Descendants
);
Maria accepts this, but if I try to use it:
select count_descendants(91402);
It prints this error message:
ERROR 1146 (42S02) at line 12: Table 'ck2.Characters' doesn't exist
"ck2" being the name of the database being used. I can post the schema if something would like to see that.
Edit: I have submitted it as a bug on MariaDB's bug tracker: MDEV-16629
This was a bug with recursive CTE queries in MariaDB, has been confirmed, and was fixed.
I couldn't find solution for this problem.
I have table A with primary key ROW_ID, table B with same extern key and column SOMETHING.
Also, I have function created like this:
CREATE FUNCTION FIND_SOMETHING_FOR_ID(ROW_ID INTEGER)
RETURNS TABLE(SOMETHING INTEGER)
BEGIN ATOMIC
RETURN
SELECT SOME_SCALAR_FUNCTION(SOMETHING)
FROM B b
WHERE b.ROW_ID=ROW_ID;
END#
The thing I want to do is: for each ROW_ID in A get table returned by FIND_SOMETHING_FOR_ID
and then get UNION of all tables.
According to the documentation, you can do what you want as:
select fsfi.*
from A a cross join
table(find_something_for_id(a.row_id)) fsfi;
That is, a table-valued function can reference tables before it in the from clause, but not after it. (Note: I replaced the , in the from with cross join because I abhor commas in the from clause.)
By the way, SQL Server solves this problem with the cross apply operator.
I have a table-valued PL/pgsql function that takes as 1 input an integer, an ID. The table that is returned has fixed columns (say 5) but varying number of rows.
There is a large table of these unique IDs. I'd like to apply this function to each ID and UNION ALL the results.
Looking online I keep seeing CROSS APPLY as the solution, but it does not appear to be available in PostgreSQL. How can I do this "apply" operation?
One trivial solution is to re-write the table-valued function with an additional outer loop. But is there a way to do this directly in SQL?
I think it's impossible to do in current version of PostgreSQL (9.2). In 9.3 there would be LATERAL join which does exactly what you want.
You can, however, apply function returning set of simple values:
select id, func(id) as f from tbl1
sql fiddle demo
SQL Fiddle
create table t (id int);
insert into t (id) select generate_series(1, 10);
create or replace function f (i integer)
returns table(id_2 integer, id_3 integer) as $$
select id * 2 as id_2, id * 3 as id_3
from t
where id between i - 1 and i + 1
$$ language sql;
select id, (f(id)).*
from t;
I need to migrate SQL queries written for MS SQL Server 2005 to Postgres 9.1.
What is the best way to substitute for CROSS APPLY in this query?
SELECT *
FROM V_CitizenVersions
CROSS APPLY
dbo.GetCitizenRecModified(Citizen, LastName, FirstName, MiddleName,
BirthYear, BirthMonth, BirthDay, ..... ) -- lots of params
GetCitizenRecModified() function is a table valued function. I can't place code of this function because it's really enormous, it makes some difficult computations and I can't abandon it.
In Postgres 9.3 or later use a LATERAL join:
SELECT v.col_a, v.col_b, f.* -- no parentheses, f is a table alias
FROM v_citizenversions v
LEFT JOIN LATERAL f_citizen_rec_modified(v.col1, v.col2) f ON true
WHERE f.col_c = _col_c;
Why LEFT JOIN LATERAL ... ON true?
Record returned from function has columns concatenated
For older versions, there is a very simple way to accomplish what I think you are trying to with a set-returning function (RETURNS TABLE or RETURNS SETOF record OR RETURNS record):
SELECT *, (f_citizen_rec_modified(col1, col2)).*
FROM v_citizenversions v
The function computes values once for every row of the outer query. If the function returns multiple rows, resulting rows are multiplied accordingly. All parentheses are syntactically required to decompose a row type. The table function could look something like this:
CREATE OR REPLACE FUNCTION f_citizen_rec_modified(_col1 int, _col2 text)
RETURNS TABLE(col_c integer, col_d text)
LANGUAGE sql AS
$func$
SELECT s.col_c, s.col_d
FROM some_tbl s
WHERE s.col_a = $1
AND s.col_b = $2
$func$;
You need to wrap this in a subquery or CTE if you want to apply a WHERE clause because the columns are not visible on the same level. (And it's better for performance anyway, because you prevent repeated evaluation for every output column of the function):
SELECT col_a, col_b, (f_row).*
FROM (
SELECT col_a, col_b, f_citizen_rec_modified(col1, col2) AS f_row
FROM v_citizenversions v
) x
WHERE (f_row).col_c = _col_c;
There are several other ways to do this or something similar. It all depends on what you want exactly.
Necromancing:
New in PostgreSQL 9.3:
The LATERAL keyword
left | right | inner JOIN LATERAL
INNER JOIN LATERAL is the same as CROSS APPLY
and LEFT JOIN LATERAL is the same as OUTER APPLY
Example usage:
SELECT * FROM T_Contacts
--LEFT JOIN T_MAP_Contacts_Ref_OrganisationalUnit ON MAP_CTCOU_CT_UID = T_Contacts.CT_UID AND MAP_CTCOU_SoftDeleteStatus = 1
--WHERE T_MAP_Contacts_Ref_OrganisationalUnit.MAP_CTCOU_UID IS NULL -- 989
LEFT JOIN LATERAL
(
SELECT
--MAP_CTCOU_UID
MAP_CTCOU_CT_UID
,MAP_CTCOU_COU_UID
,MAP_CTCOU_DateFrom
,MAP_CTCOU_DateTo
FROM T_MAP_Contacts_Ref_OrganisationalUnit
WHERE MAP_CTCOU_SoftDeleteStatus = 1
AND MAP_CTCOU_CT_UID = T_Contacts.CT_UID
/*
AND
(
(__in_DateFrom <= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateTo)
AND
(__in_DateTo >= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateFrom)
)
*/
ORDER BY MAP_CTCOU_DateFrom
LIMIT 1
) AS FirstOE
I like Erwin Brandstetter's answer however, I've discovered a performance problem:
when running
SELECT *, (f_citizen_rec_modified(col1, col2)).*
FROM v_citizenversions v
The f_citizen_rec_modified function will be ran 1 time for every column it returns (multiplied by every row in v_citizenversions). I did not find documentation for this effect, but was able to deduce it by debugging. Now the question becomes, how can we get this effect (prior to 9.3 where lateral joins are available) without this performance robbing side effect?
Update: I seem to have found an answer. Rewrite the query as follows:
select x.col1, x.col2, x.col3, (x.func).*
FROM (select SELECT v.col1, v.col2, v.col3, f_citizen_rec_modified(col1, col2) func
FROM v_citizenversions v) x
The key difference being getting the raw function results first (inner subquery) then wrapping that in another select that busts those results out into the columns. This was tested on PG 9.2
This link appears to show how to do it in Postgres 9.0+:
PostgreSQL: parameterizing a recursive CTE
It's further down the page in the section titled "Emulating CROSS APPLY with set-returning functions". Please be sure to note the list of limitations after the example.
If I try to create a column whose value is a select returning more than one row, I get an error.
=> select (select 1 union select 2);
ERROR: more than one row returned by a subquery used as an expression
But if I create a function that does the same thing, I get the behavior I want.
=> create or replace function onetwo() returns setof integer as $$
$> select 1 union select 2
$> $$ language 'sql' strict immutable;
CREATE FUNCTION
=> select onetwo();
onetwo
--------
1
2
Why the difference?
It's not an apples to apples comparison.
select *
FROM (select 1
union ALL
select 2)
...is equivalent to your function.
A column in the SELECT clause can only return a single value per record. Which is impossible with your UNION example. So I converted it into a derived table/inline view, which is what is happening with the function example.
While OMG Ponies answer is entirely correct I'd rather put it like this: You're confusing SELECT f() with SELECT literal.
SELECT f() executes a function and returns its result. And, a table returning function can also be written as SELECT * FROM f() -- which is even more elegant. Because Pg doesn't yet have stored procedures -- less scheduling they can be done through functions -- we use SELECT as Microsoft SQL uses EXECUTE
SELECT LITERAL is a method of returning a literal (something that can fit in a row/column).