Reference a parameter in Postgres function - sql

The table org has a column called npi. Why does query1 work and query2 not?
Query 1 -
CREATE OR REPLACE FUNCTION check (npi TEXT)
RETURNS BOOLEAN AS $$
DECLARE
pass_npi TEXT;
BEGIN
pass_npi := npi;
SELECT 1
FROM org doc
WHERE doc.npi = pass_npi
;
RETURN 1;
END $$
Query 2 -
CREATE OR REPLACE FUNCTION check (npi TEXT)
RETURNS BOOLEAN AS $$
BEGIN
SELECT 1
FROM org doc
WHERE doc.npi = npi
;
RETURN 1;
END $$
ERROR -
Ambigious column name NPI

Because in the second case it is unclear if npi is the table column (that would be a valid, if useless statement) or the function parameter.
There are three solutions apart from the one in your first query:
The best one: use function parameters that have names different from table columns. This can be done by using a prefix:
CREATE FUNCTION check (p_npi TEXT) RETURNS boolean AS
...
SELECT ...
WHERE doc.npi = p_npi
Use the ALIAS command to “rename” the parameter:
CREATE FUNCTION check (npi TEXT) RETURNS boolean AS
$$DECLARE
p_npi ALIAS FOR npi;
BEGIN
...
SELECT ...
WHERE doc.npi = p_npi
Qualify the parameter with the function name:
CREATE FUNCTION check (npi TEXT) RETURNS boolean AS
...
SELECT ...
WHERE doc.npi = check.npi

What happens is that in Query2 you are comparing the field the doc.npi field with it, it is the same to say doc.npi and to say npi, for that reason it shows you that the sentence is ambiguous, on the contrary case in Query1 you are comparing the doc.npi field with a different field that is the pass_npi.
To solve this problem you must compare the same columns but from different tables or different columns from the same table.
Query2:
CREATE OR REPLACE FUNCTION check (npi TEXT)
RETURNS BOOLEAN AS $$
BEGIN
SELECT 1
FROM org doc
WHERE doc.npi = pass_npi
;
RETURN 1;
END $$

Related

How to add column inside postgres function without saving it to the db?

I have a postgres DB and I want to create a function that returns a copy of my table with a new column that has a value of 1 if its id is inside the array(idds[]) that the function gets as an input.
In the code below I've try to create a temporary table(chosen) that have id if it's in the idds array and to manually add the isChosen column that obviously doesn't work...
CREATE OR REPLACE FUNCTION public.getTableFromArray(idds integer[])
RETURNS table(
id INTEGER,
isChosen INTEGER
)
LANGUAGE 'plpgsql'
AS $BODY$
begin
with chosen AS(SELECT id,isChosen=1 FROM table1 WHERE ARRAY[table1.id] <# idds)
return query
SELECT id FROM table1 LEFT JOIN chosen ON table1.id=chosen.id;
end;
$BODY$;
Or, with a lot less noise, a proper boolean output column, and without the unhelpful CaMeL case identifiers in a plain SQL function:
CREATE OR REPLACE FUNCTION public.get_table_from_array(idds integer[])
RETURNS TABLE(id int, is_chosen bool)
LANGUAGE sql AS
'SELECT t.id, t.id = ANY(idds) FROM table1 t';
Might as well just run the SQL command directly, though:
SELECT id, id = ANY('{1,2,3}'::int[]) AS is_chosen FROM table1;
you can use this query instead :
select * , case when ARRAY[table1.id] <# idds then 1 else 0 end as choosen FROM table1;
so:
CREATE OR REPLACE FUNCTION public.getTableFromArray(idds integer[])
RETURNS table(
id INTEGER,
isChosen INTEGER
)
LANGUAGE 'plpgsql'
AS $BODY$
begin
return query
select id , case when ARRAY[table1.id] <# idds then 1 else 0 end as isChosen FROM table1;
end;
$BODY$;

Challenging Postgres sql searching in text array by any value from text array

so here is my problem to make a query to work, it's extremelely challenging to make it, I was using unnest, searching using #>, nothing works.
I have a table in db that has text[] field, and I am passing text[] field to function that any of the values from it should return the row, or if I don't pass the value it should return all of them. For example:
table:
id (int)
names (text[])
data in db:
id names
---------------------------------
1 { alex }
2 { tom, john }
3 { tom, alex }
4 { rocky, simon, leon, john }
how it should work?
when I pass {simon} it should return only row 4
when I pass {alex} it should return rows 1 and 3
when I pass {tom,leon} it should return rows 2, 3 and 4
when I pass null, it should return rows 1,2,3,4
any in parameter that matches any value from row, should return row
CREATE OR REPLACE FUNCTION public.get_names
(
_names TEXT[]
)
RETURNS JSONB
LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
RETURN (SELECT
json_agg(t)
FROM
(
SELECT
n.id,
n.names
FROM
public.names n
WHERE
_names IS NULL
OR exists (select unnest(cast(_names as text[]))) = ANY (n.names)
) t);
END;
$BODY$;
I had no success on any of my queries getting errors like:
operator does not exist: boolean = character varying
ANY/ALL (array) does not support set arguments
More than one row returned by a subquery used as an expression
and more..
as you can see I was really trying to make it work, but to me it's super hard and I need some of your help.
The overlaps operator && will do what you want
select *
from test
where names && array['tom', 'leon']
The above will return all rows where names contains at least one element from the right hand side.
To deal with a NULL parameter you can use a NULL check in your function
CREATE OR REPLACE FUNCTION public.get_names (_names TEXT[] default null )
RETURNS JSONB
LANGUAGE sql
AS
$BODY$
SELECT jsonb_agg(t)
FROM (
SELECT n.id, n.names
FROM public.test n
WHERE _names is null
OR n.names && _names
) t;
$BODY$;
Write your function this way using && operator:
CREATE OR REPLACE FUNCTION public.get_names (_names TEXT[] default null )
RETURNS JSONB
LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
RETURN
(SELECT
json_agg(t)
FROM
(
SELECT
id,
names
FROM
public.test n
WHERE
case
when _names IS not NULL then names && _names
else 1=1
end
) t);
END;
$BODY$;
DEMO

Returning table with specific parameters from a PostgreSQL function

I have this function, with which I would like to return a table, which holds two columns: game_name and follow (this would be an integer 0 or 1):
CREATE OR REPLACE FUNCTION public.toggle2(uid numeric, gid NUMERIC)
RETURNS TABLE (
follow INT,
game_name TEXT
)
LANGUAGE plpgsql
AS $$
BEGIN
IF NOT EXISTS(SELECT *
FROM game_follows
WHERE user_id = uid and game_id = gid)
THEN
INSERT INTO game_follows(user_id, game_id) VALUES(uid, gid);
follow := 1;
ELSE
DELETE FROM game_follows WHERE user_id = uid and game_id = gid;
follow := 0;
END IF;
SELECT name INTO game_name FROM games WHERE id = gid;
END;
$$
;
Sadly, the function returns empty values. I am using it as this:
SELECT * FROM toggle2(83, 12);
A function declared to RETURN TABLE can return 0-n rows.
You must actively return rows, or nothing will be returned (no row). One way to do this:
RETURN NEXT; -- as last line before END;
There are other ways, see the manual.
However, it seems you want to return exactly one row every time. So rather use OUT parameters:
CREATE OR REPLACE FUNCTION public toggle2(uid numeric, gid numeric, OUT follow int, OUT game_name text) AS ...
Then it's enough to assign those OUT parameters, they are returned in the single result row automatically.
See:
Returning from a function with OUT parameter
plpgsql error "RETURN NEXT cannot have a parameter in function with OUT parameters" in table-returning function
How to return result of a SELECT inside a function in PostgreSQL?

<column> is ambiguous in column comparison between two tables

I want this postgres function to work :
CREATE OR REPLACE FUNCTION difference_of_match_ids_in_match_history_and_match_results()
returns table(match_id BIGINT)
as
$$
BEGIN
return QUERY
SELECT *
FROM sports.match_history
WHERE match_id NOT IN (SELECT match_id
FROM sports.match_results);
END $$
LANGUAGE 'plpgsql';
This stand alone query works just fine:
SELECT *
FROM sports.match_history
WHERE match_id NOT IN (SELECT match_id FROM sports.match_results);
But when I put it into this function and try to run it like this:
select *
from difference_of_match_ids_in_match_history_and_match_results();
I get this:
SQL Error [42702]: ERROR: column reference "match_id" is ambiguous
Detail: It could refer to either a PL/pgSQL variable or a table
column. Where: PL/pgSQL function
difference_of_match_ids_in_match_history_and_match_results() line 3 at
RETURN QUERY
I've seen other questions with this same error, and they suggest naming the sub queries to specify which instance of a column you're referring to, however, those examples use joins and my query works fine outside of the function.
If I do need to name the column, how would I do so with only one sub-query?
If that isn't the issue, then I'm assuming that there's something wrong with the way I'm defining a function.
You query is fine. The ambiguity is on the match_id in returns table(match_id BIGINT) rename it or prefix the columns with the table name in your query
CREATE OR REPLACE FUNCTION difference_of_match_ids_in_match_history_and_match_results()
returns table(new_name BIGINT)
as
$$
BEGIN
return QUERY
SELECT *
FROM sports.match_history
WHERE match_id NOT IN (SELECT match_id
FROM sports.match_results);
END $$
LANGUAGE 'plpgsql';
or
CREATE OR REPLACE FUNCTION difference_of_match_ids_in_match_history_and_match_results()
returns table(match_id BIGINT)
as
$$
BEGIN
return QUERY
SELECT sports.match_history.match_id
FROM sports.match_history
WHERE sports.match_history.match_id NOT IN (SELECT sports.match_results.match_id
FROM sports.match_results);
END $$
LANGUAGE 'plpgsql';
Didn't test the code.
The structure of the result set must match the function result type. If you want to get only match_ids:
CREATE OR REPLACE FUNCTION difference_of_match_ids_in_match_history_and_match_results()
RETURNS TABLE(m_id BIGINT) -- !!
AS
$$
BEGIN
RETURN QUERY
SELECT match_id -- !!
FROM sports.match_history
WHERE match_id NOT IN (SELECT match_id
FROM sports.match_results);
END $$
LANGUAGE 'plpgsql';
If you want to get whole rows as a result:
DROP FUNCTION difference_of_match_ids_in_match_history_and_match_results();
CREATE OR REPLACE FUNCTION difference_of_match_ids_in_match_history_and_match_results()
RETURNS SETOF sports.match_history -- !!
AS
$$
BEGIN
RETURN QUERY
SELECT * -- !!
FROM sports.match_history
WHERE match_id NOT IN (SELECT match_id
FROM sports.match_results);
END $$
LANGUAGE 'plpgsql';
As others have answerd, it's an ambiguity between the result definition and PL/pgSQL variables. The column name in a set returning function is in fact also a variable inside the function.
But you don't need PL/pgSQL for this in the first place. If you use a plain SQL function it will be more efficient and the problem will go away as well:
CREATE OR REPLACE FUNCTION difference_of_match_ids_in_match_history_and_match_results()
returns table(match_id BIGINT)
as
$$
SELECT match_id --<< do not return * - only return one column
FROM sports.match_history
WHERE match_id NOT IN (SELECT match_id
FROM sports.match_results);
$$
LANGUAGE sql;
Note that the language name is an identifier and should not be quoted at all.
The naming conflict between column names and plpgsql OUT parameters has been addressed. More details here:
Postgresql - INSERT RETURNING INTO ambiguous column reference
I would also use a different query style. NOT IN (SELECT ...) is typically slowest and carries traps with NULL values. Use NOT EXISTS instead:
SELECT match_id
FROM sports.match_history h
WHERE NOT EXISTS (
SELECT match_id
FROM sports.match_results
WHERE match_id = h.match_id
);
More:
Select rows which are not present in other table

Passing a ResultSet into a Postgresql Function

Is it possible to pass the results of a postgres query as an input into another function?
As a very contrived example, say I have one query like
SELECT id, name
FROM users
LIMIT 50
and I want to create a function my_function that takes the resultset of the first query and returns the minimum id. Is this possible in pl/pgsql?
SELECT my_function(SELECT id, name FROM Users LIMIT 50); --returns 50
You could use a cursor, but that very impractical for computing a minimum.
I would use a temporary table for that purpose, and pass the table name for use in dynamic SQL:
CREATE OR REPLACE FUNCTION f_min_id(_tbl regclass, OUT min_id int) AS
$func$
BEGIN
EXECUTE 'SELECT min(id) FROM ' || _tbl
INTO min_id;
END
$func$ LANGUAGE plpgsql;
Call:
CREATE TEMP TABLE foo ON COMMIT DROP AS
SELECT id, name
FROM users
LIMIT 50;
SELECT f_min_id('foo');
Major points
The first parameter is of type regclass to prevent SQL injection. More info in this related answer on dba.SE.
I made the temp table ON COMMIT DROP to limit its lifetime to the current transaction. May or may not be what you want.
You can extend this example to take more parameters. Search for code examples for dynamic SQL with EXECUTE.
-> SQLfiddle demo
I would take the problem on the other side, calling an aggregate function for each record of the result set. It's not as flexible but can gives you an hint to work on.
As an exemple to follow your sample problem:
CREATE OR REPLACE FUNCTION myMin ( int,int ) RETURNS int AS $$
SELECT CASE WHEN $1 < $2 THEN $1 ELSE $2 END;
$$ LANGUAGE SQL STRICT IMMUTABLE;
CREATE AGGREGATE my_function ( int ) (
SFUNC = myMin, STYPE = int, INITCOND = 2147483647 --maxint
);
SELECT my_function(id) from (SELECT * FROM Users LIMIT 50) x;
It is not possible to pass an array of generic type RECORD to a plpgsql function which is essentially what you are trying to do.
What you can do is pass in an array of a specific user defined TYPE or of a particular table row type. In the example below you could also swap out the argument data type for the table name users[] (though this would obviously mean getting all data in the users table row).
CREATE TYPE trivial {
"ID" integer,
"NAME" text
}
CREATE OR REPLACE FUNCTION trivial_func(data trivial[])
RETURNS integer AS
$BODY$
DECLARE
BEGIN
--Implementation here using data
return 1;
END$BODY$
LANGUAGE 'plpgsql' VOLATILE;
I think there's no way to pass recordset or table into function (but I'd be glad if i'm wrong). Best I could suggest is to pass array:
create or replace function my_function(data int[])
returns int
as
$$
select min(x) from unnest(data) as x
$$
language SQL;
sql fiddle demo