Storing variable - postgresql

Is there any way in postgresql to store the value of a particular column into a variable.
Eg.
name | age
|
John | 19
Phill | 20
Palmer | 25
I would like to store 25 into a variable I can reference in the future.

Sure. Use stored procedures. http://www.postgresql.org/docs/current/static/plpgsql.html
Trivial example:
CREATE OR REPLACE FUNCTION JohnName()
RETURNS numeric AS
$BODY$
DECLARE num numeric;
BEGIN
select age into num from yourtable where name = 'John';
num := num * 2;
RETURN num;
END
$BODY$
LANGUAGE plpgsql VOLATILE;
select * from JohnName()

Related

How to return result of dynamic SELECT inside a function in PostgreSQL?

A very similar question here but not quite the same as this one.
I have a function that uses IF statements to determine what type of SELECT query to return.
How can I declare what a CREATE FUNCTION statment should return when I will never know the exact columns a SELECT query within it might return? That is, I can't setup a RETURNS TABLE declaration with a list of columns because I don't know which columns might come back. All I know is that I definitely will want a table of results to be returned.
Here is my function (uncompleted, pseudo):
CREATE OR REPLACE FUNCTION functiona(_url character varying DEFAULT NULL)
RETURNS -- what type? if TABLE how do I know what columns to specify
LANGUAGE plpgsql
AS
$$
DECLARE
_urltypeid int;
BEGIN
IF _url IS NOT NULL
THEN
_urltypeid := reference.urltype(_url);
IF _urltypeid = 1
THEN
RETURN QUERY
SELECT location, auxiliary, response FROM tablea -- unique columns from one table
END IF;
IF _urltypeid = 2
THEN
RETURN QUERY
SELECT ip, location, host, authority FROM tableb -- unique columns from another table
END IF;
END IF;
END;
$$;
I come from a MS SQL Server background where I don't have to specify in the CREATE FUNCTIONstatement what I'm returning, hence this is very confusing for me.
Not an answer, but an explanation of why answer from #JonathanJacobson will not work using a simple example:
\d animals
Table "public.animals"
Column | Type | Collation | Nullable | Default
--------+------------------------+-----------+----------+---------
id | integer | | not null |
cond | character varying(200) | | not null |
animal | character varying(200) | | not null |
CREATE OR REPLACE FUNCTION public.animal(a_type character varying)
RETURNS record
LANGUAGE plpgsql
AS $function$
BEGIN
SELECT row(id, cond, animal) FROM animals where animal = a_type;
END;
$function$
select * from animal('cat');
ERROR: a column definition list is required for functions returning "record"
LINE 1: select * from animal('cat');
CREATE OR REPLACE FUNCTION public.animal(a_type character varying)
RETURNS SETOF record
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT id, cond, animal FROM animals where animal = a_type;
END;
$function$
;
select * from animal('cat') as t(i integer, c varchar, a varchar);
i | c | a
---+------+-----
1 | fat | cat
2 | slim | cat
6 | big | cat
In order to use the output of a function returning a record or setof record you need to declare the output fields and types when you run the function.
You could use the record type. Not tested.
CREATE OR REPLACE FUNCTION functiona(_url character varying DEFAULT NULL)
RETURNS record
LANGUAGE plpgsql
AS
$$
DECLARE
_broadcasttypeid int;
BEGIN
IF _url IS NOT NULL
THEN
_urltypeid := reference.urltype(_url);
IF _urltypeid = 1
THEN
RETURN
(SELECT row(location, auxiliary, response) FROM tablea);
END IF;
IF _urltypeid = 2
THEN
RETURN
(SELECT row(ip, location, host, authority) FROM tableb);
END IF;
END IF;
END;
$$;
Other composite types, such as jsonb and hstore are also a solution.

function to provide id of interval the input is located between

I want to create a function that will receive a date as input and be able to provide back the id of the corresponding interval that it's located in.
For example with this table here:
id | start | end
-- +------------+------------
1 | 2000-11-30 | 2001-02-19
2 | 2001-02-21 | 2001-06-04
3 | 2001-06-05 | 2001-07-13
4 | 2001-07-15 | 2001-11-29
If i input the date '2001-04-17', i want it to return back the id value of 2.
i'm currently trying this currently but can't get it to work:
create or replace function getId(_date date) returns integer
as $$
declare
myId integer;
begin
set myId = (select id from myTable
where ((_date >= start) and (_date <= end)));
return myId;
end;
$$ language plpgsql
;
You can return id directly without setting to myId. Andend is the PostgreSQL keyword, so you should use it inside double quotes ""
create or replace function getId(_date date) returns integer
as $$
begin
return (select id from myTable
where ((_date >= start) and (_date <= "end")));
end;
$$ language plpgsql
;
Call the function:
select getId('2001-04-17');
Output: 2

Multiple result sets from a stored procedure with PostgreSQL

Getting unexpected result from function. I just need two result sets from the code that I have written in the function but instead getting some unnamed portal issue.
I have tried same using cursor.Which is as follows.
CREATE OR REPLACE FUNCTION User(param_state CHAR(10)) RETURNS SETOF refcursor AS $$
DECLARE
ref1 refcursor; -- Declare cursor variables
ref2 refcursor;
BEGIN
OPEN ref1 FOR select * from Table1
WHERE code = param_state;
RETURN NEXT ref1;
OPEN ref2 FOR select * from Table2
WHERE code= param_state;
RETURN NEXT ref2;
END;
$$ LANGUAGE plpgsql;
Expected output should be to 2 result set of 2 column each
-------------------
|party_code | limit|
|------------------|
|T001 | 120 |
-------------------
-------------------
|party_code | Sal |
|------------------|
|T001 | 1000 |
-------------------
But the output is
---------------------
|<unnamed portal 34>|
---------------------
|<unnamed portal 35>|
Have you tried to name your cursors ..
...
DECLARE
ref1 refcursor := 'mycursor1' ;
ref2 refcursor := 'mycursor2' ;
...
.. and fetch the results using their names ..
SELECT * FROM "User"('T001');
BEGIN;
FETCH ALL FROM mycursor2;
FETCH ALL FROM mycursor1;
END;
FETCH ALL FROM mycursor2;
code | Sal
------+------
T001 | 1000
(1 row)
postgres=# FETCH ALL FROM mycursor1;
code | limit
------+-------
T001 | 120
(1 row)

Improving PL/pgSQL function

I just finished writing my first PLSQL function. Here what it does.
The SQL function attempt to reset the duplicate timestamp to NULL.
From table call_records find all timestamp that are duplicated.(using group by)
loop through each timestamp.Find all record with same timestamp (times-1, so that only 1 record for a given times is present)
From all the records found in step 2 update the timestamp to NULL
Here how the SQL function looks like.
CREATE OR REPLACE FUNCTION nullify() RETURNS INTEGER AS $$
DECLARE
T call_records.timestamp%TYPE;
-- Not sure why row_type does not work
-- R call_records%ROWTYPE;
S integer;
CRNS bigint[];
TMPS bigint[];
sql_stmt varchar = '';
BEGIN
FOR T,S IN (select timestamp,count(timestamp) as times from call_records where timestamp IS NOT NULL group by timestamp having count(timestamp) > 1)
LOOP
sql_stmt := format('SELECT ARRAY(select plain_crn from call_records where timestamp=%s limit %s)',T,S-1);
EXECUTE sql_stmt INTO TMPS;
CRNS := array_cat(CRNS,TMPS);
END LOOP;
sql_stmt = format('update call_records set timestamp=null where plain_crn in (%s)',array_to_string(CRNS,','));
RAISE NOTICE '%',sql_stmt;
EXECUTE sql_stmt ;
RETURN 1;
END
$$ LANGUAGE plpgsql;
Help me understand more PL/pgSQL language my suggesting me how it can be done better.
#a_horse_with_no_name: Here how the DB structure looks like
\d+ call_records;
id integer primary key
plain_crn bigint
timestamp bigint
efd integer default 0
id | efd | plain_crn | timestamp
----------+------------+------------+-----------
1 | 2016062936 | 8777444059 | 14688250050095
2 | 2016062940 | 8777444080 | 14688250050095
3 | 2016063012 | 8880000000 | 14688250050020
4 | 2016043011 | 8000000000 | 14688240012012
5 | 2016013011 | 8000000001 | 14688250050020
6 | 2016022011 | 8440000001 |
Now,
select timestamp,count(timestamp) as times from call_records where timestamp IS NOT NULL group by timestamp having count(timestamp) > 1
timestamp | count
-----------------+-----------
14688250050095 | 2
14688250050020 | 2
All that I want is to update the duplicate timestamp to null so that only one of them record has the given timestamp.
In short the above query should return result like this
select timestamp,count(timestamp) as times from call_records where timestamp IS NOT NULL group by timestamp;
timestamp | count
-----------------+-----------
14688250050095 | 1
14688250050020 | 1
You can use array variables directly (filter with predicate =ANY() - using dynamic SQL is wrong for this purpose:
postgres=# DO $$
DECLARE x int[] = '{1,2,3}';
result int[];
BEGIN
SELECT array_agg(v)
FROM generate_series(1,10) g(v)
WHERE v = ANY(x)
INTO result;
RAISE NOTICE 'result is: %', result;
END;
$$;
NOTICE: result is: {1,2,3}
DO
Next - this is typical void function - it doesn't return any interesting. Usually these functions returns nothing when all is ok or raises exception. The returning 1 RETURN 1 is useless.
CREATE OR REPLACE FUNCTION foo(par int)
RETURNS void AS $$
BEGIN
IF EXISTS(SELECT * FROM footab WHERE id = par)
THEN
...
ELSE
RAISE EXCEPTION 'Missing data for parameter: %', par;
END IF;
END;
$$ LANGUAGE plpgsql;

How to split a type into multiple columns in Postgres?

I have the following code to return multiple values from pl/python:
CREATE TYPE named_value AS (
name text,
value integer
);
CREATE or replace FUNCTION make_pair (name text, value integer)
RETURNS named_value
AS $$
return [ name, value ]
$$ LANGUAGE plpythonu;
select make_pair('egg', 4) as column;
The output is:
column
(egg,4)
What I want to do is to split the output into two separate columns. Like this:
column, column2
egg, 4
How do I do this? Googled for 1 hour got me nowhere. So I hope I will add some search keywords in the end:
multiple return values multiple results multiple columns unnest list unnest set
Yeah, the syntax for this is a bit wacky, requiring extra parentheses:
select (make_pair('egg', 4)).name
To get multiple components from the output while only invoking the function once, you can use a sub-select:
select (x.column).name, (x.column).value from (select make_pair('egg', 4) as column) x;
SELECT * FROM make_pair('egg', 4);
and some variants:
SELECT name, value FROM make_pair('egg', 4) AS x;
SELECT a, b FROM make_pair('egg', 4) AS x(a,b);
A solution I found was to use join:
create table tmp (a int, b int, c int);
insert into tmp (a,b,c) values (1,2,3), (3,4,5), (5,12,13);
create type ispyth3 as (is_it boolean, perimeter int);
create function check_it(int, int, int) returns ispyth3 as $$
begin
return ($1*$1 + $2*$2 = $3*$3, $1+$2+$3);
end
$$ language plpgsql;
select * from tmp join check_it(a,b,c) on 1=1;
This returns:
a | b | c | is_it | perimeter
---+----+----+-------+-----------
1 | 2 | 3 | f | 6
3 | 4 | 5 | t | 12
5 | 12 | 13 | t | 30
(3 rows)
The following is working code to avoid having to run the function twice and at the same time avoid a subquery.
CREATE TYPE named_value AS (
name text,
value integer
);
CREATE or replace FUNCTION setcustomvariable(variablename text, variablevalue named_value)
RETURNS named_value
AS $$
GD[variablename] = variablevalue
return variablevalue
$$ LANGUAGE plpythonu;
CREATE or replace FUNCTION getcustomvariable(variablename text)
RETURNS named_value
AS $$
return GD[variablename]
$$ LANGUAGE plpythonu;
CREATE or replace FUNCTION make_pair (name text, value integer)
RETURNS named_value
AS $$
return [ name, value ]
$$ LANGUAGE plpythonu;
select setcustomvariable('result', make_pair('egg', 4)), (getcustomvariable('result')).name, (getcustomvariable('result')).value