How to declare a variable in postgres script? - postgresql

I am fairly new to Postgres and I cannot believe how difficult I am finding just to declare a variable. I did come across other SO posts, but none of them helped in my situation. All I want is to write the a script like below in postgres:
declare #age int = 10;
select * from person p where p.age > #age;
Based on the SO post here, I tried:
DO
$$
DECLARE
overTheAgeOf int := 15;
BEGIN
select *
from person
where age > overTheAgeOf;
END
$$;
This gives me error: [42601] ERROR: query has no destination for result data
Then I tried returning the result of the script:
return (select *
from person
where age > overTheAgeOf);
That gave me another error: ERROR: RETURN cannot have a parameter in function returning void
How do declare a variable and use it in script(s) that follows?

You are confused on several levels.
There is the query language SQL, and there is the procedural language PL/pgSQL. The only connection is that
you can run SQL statements from PL/pgSQL code
you can have PL/pgSQL code in the body of the SQL statements DO and CREATE FUNCTION/PROCEDURE.
There are variables in PL/pgSQL, which are defined in the DECLARE section, but there are no variables in SQL.
DO statements cannot return any values.
If you want to use PL/pgSQL variables, and you want to return values, you'll have to use a function. An example:
CREATE FUNCTION getpersons() RETURNS SETOF person
LANGUAGE plpgsql AS
$$DECLARE
overTheAgeOf int := 15;
BEGIN
RETURN QUERY
SELECT *
FROM person
WHERE age > overTheAgeOf;
END;$$;
SELECT getpersons();
There is the alternative of using variables on the client. With the psql client, you could use:
\set overTheAgeOf 15
SELECT *
FROM person
WHERE age > :overTheAgeOf;

The good structure is like this :
DO
LANGUAGE plpgsql $$
DECLARE
variable int := 0;
BEGIN
-- your code
raise notice '%', variable::varchar;
END;
$$;

Related

Can anybody please assist with syntax in a function in postgresql (from mysql)

I am trying to create the following function in PostgreSQL but get the following error. This is from a MySQL procedure that I need to convert to PostgreSQL. I am failing to convert the syntax to PostgreSQL. I am a beginner in PostgreSQL. Please assist me.
CREATE OR REPLACE FUNCTION public.usp_failed_analyze4()
RETURNS TABLE(status varchar) as
$BODY$
SET #maxdate = (SELECT MAX(analyzetime) FROM wp_analyze_history);
SET #maxdateint = (SELECT DATEDIFF(NOW() ,MAX(analyzetime)) FROM wp_analyze_history);
SET #STATUS = SELECT Status from wp_analyze_history WHERE Status NOT IN ('OK','Table is already up to date','The Analyze task DID NOT run!') AND analyzetime = #maxdate);
SET #STATUSNOTRUN = 'The Analyze task DID NOT run!';
IF #maxdateint > 7
THEN SELECT #STATUSNOTRUN;
ELSE SELECT #STATUS as "";
$BODY$
LANGUAGE sql;
error: ERROR: syntax error at or near "#"
Position: 109
It's hard to tell what you want as you tried to copy the MySQL 1:1.
However, there are several problems in your code:
language sql does not have variables or IF statements. You need to use PL/pgSQL (language plpgsql)
PL/pgSQL requires a declare block to declare all variables and the actual code needs a begin ... end; block as well.
You can use SET for assignment
To store the result of a single row query in a variable use select ... into ... from
The character # is invalid in an SQL identifier and can't be used for variable names (which follow the rules of SQL identifiers). In Postgres it's a common habit to prefix variable with something to avoid ambiguity with column names. I use l_ for local variables (but that's completely a personal preference)
You don't seem to want to return multiple rows, but a single value. So you don't need returns table
To return something from a function, use return not select
Putting that all together it should look something like this:
CREATE OR REPLACE FUNCTION usp_failed_analyze4()
RETURNS varchar -- return a single value
AS
$BODY$
declare
l_maxdate timestamp;
l_maxdatediff interval;
l_status text;
l_statusnotrun text;
begin
select MAX(analyzetime), current_timestamp - MAX(analyzetime)
into l_maxdate, l_maxdatediff
FROM wp_analyze_history;
SELECT Status
into l_status
from wp_analyze_history
WHERE Status NOT IN ('OK','Table is already up to date','The Analyze task DID NOT run!')
AND analyzetime = l_maxdate;
l_statusnotrun := 'The Analyze task DID NOT run!';
IF l_maxdatediff > interval '7 days'
THEN
return l_statusnotrun;
ELSE
return ''; -- strings are enclosed in single quotes in SQL
end if;
end;
$BODY$
LANGUAGE plpgsql;
There is still room for a lot of optimization, but this matches your initial code as much as possible.

Why can you select a calculation and get a result, but can't get a result when selecting a function that has the same calculation?

In plpgsql, I'm trying to test a calculation. To see a result in my sql editor, I can do the following:
select 3 * 5;
Then when I highlight the code in my editor and run the query, I get the result ?column? with a row with the value 15.
But if a create a function like this:
CREATE OR REPLACE FUNCTION do_something() RETURNS VOID AS $$
BEGIN SELECT 3 * 5; END;
$$ LANGUAGE PLPGSQL;
and then highlight and run the query:
SELECT do_something();
I get an error saying that the "query has no destination for result data".
I just want to be able to test blocks of code and see results even if it's not associated with a table. What's the best way to do that?
Your first error is that the function is declared to return nothing return void. If you want to return an integer, you need to change that to return int.
Additionally in PL/pgSQL, you need to use return to return a value, not a SELECT statement.
CREATE OR REPLACE FUNCTION do_something()
RETURNS int
AS $$
BEGIN
return 3 * 5;
END;
$$ LANGUAGE PLPGSQL;
Note that for a simple calculation like that a language SQL function is more efficient, as PL/pgSQL does have some overhead.
CREATE OR REPLACE FUNCTION do_something()
RETURNS int
AS $$
select 3 * 5;
$$ LANGUAGE sql;

Select query by declare variable on postgresql

I am new working postgresql and pgadmin4.I write a very simple query.
I have a table called PgFinalLocationsTable on public schema.In my table there are a few filed.One of these filed is UserName .I want to declare a variable and finally do select on my table according this variable like below:
DO $$
DECLARE myvar text default 'sa';
BEGIN
select * from public."PgFinalLocationsTable" where "UserName" = myvar;
END $$;
But why i got these message:
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function inline_code_block line 4 at SQL statement
SQL state: 42601
It is a simple query!!!
After googling and see post on stack i have changed my query like this:
CREATE OR REPLACE FUNCTION fun(text myvar) RETURNS text AS $$
--DECLARE myvar text;
BEGIN
select * from public."PgFinalLocationsTable" where "UserName" = myvar;
END;
$$ language plpgsql;
select fun('sa');
I want to return all my fields and i do not want to use plpgsql.I want to use PostgreSQL. In any case i got this error:
ERROR: type myvar does not exist
SQL state: 42704
What is the problem on my first query and second query?Should i have make a function for select query when i want to pass a variable?
I do all stuff because i want to create this sql query:
"IF (NOT EXISTS(SELECT 1 FROM [dbo].[{0}] WHERE [UserId] = #UserId And [DeviceId] = #DeviceId)) " +
"BEGIN " +
"INSERT INTO [dbo].[{0}]([Id], [Location], [Timestamp], [UserId], [DeviceId], [AllowDomains], [Topic], [UserName], [FirstName], [LastName], [JobLocationName], [LocationId], [AppVersion], [AppName]) " +
"VALUES(#Id, GEOGRAPHY::Point(#X, #Y, 4326), #Timestamp, #UserId, #DeviceId, #AllowDomains, #Topic, #UserName, #FirstName, #LastName, #JobLocationName, #LocationId, #AppVersion, #AppName) " +
"END "
You don't understand to DO command well. DO command is anonymous function without declaration, and because it has not declared an output, then is not possible any other result than debug stream.
so your first example has not sense in PostgreSQL. Result of unbind queries in MSSQL is returned as result of MS SQL procedure. Nothing similar is possible in PostgreSQL. PostgreSQL knows only functions, that can returns scalar value, composite value or relation (only one). When you are coming from MS SQL, the best what you can, try to forgot almost all knowleadge from MS SQL.
ERROR: type myvar does not exist
SQL state: 42704
This bug is clean - you switch variable name and type name - really type myvar doesn't exist.
Some function that returns table can looks like:
CREATE OR REPLACE FUNCTION fx1(myvar text)
RETURNS SETOF public."PgFinalLocationsTable" AS $$
BEGIN
RETURN QUERY SELECT * FROM public."PgFinalLocationsTable" WHERE "UserName" = myvar;
END;
$$ LANGUAGE plpgsql;
or you can use a SQL language only
CREATE OR REPLACE FUNCTION fx1(myvar text)
RETURNS SETOF public."PgFinalLocationsTable" AS $$
SELECT * FROM public."PgFinalLocationsTable" WHERE "UserName" = $1;
$$ LANGUAGE sql;
Because PostgreSQL doesn't support unbind queries, then doesn't allow it. You should to use RETURN QUERY command - in PLpgSQL language.
Because programming with stored procedures is really different between PostgreSQL and MSSQL (MSSQL is not similar to any other), please, try to read documentation - it is not bad https://www.postgresql.org/docs/current/static/plpgsql.html
Your function can looks in Postgres like (I don't know used types)
CREATE OR REPLACE FUNCTION fx("_UserId" int,
"_DeviceId" int,
"_X" int,
"_Y" int,
...
BEGIN
IF NOT EXISTS(SELECT * FROM /* I don't know what [{0}] means */
WHERE "UserId" = "_UserId" AND "DeviceId" = "_DeviceId")
THEN
INSERT INTO ..
END IF;
END;
$$ LANGUAGE plpgsql;
Probably your fragment can be solved without procedural extension by INSERT INTO ON CONFLICT DO NOTHING command https://www.postgresql.org/docs/current/static/sql-insert.html - what is better.
Note - using case sensitive identifiers is short way to hell.

Using script variables in sql-do session not works?

It is not a psql terminal, I can't use \set myvariable value as suggested here, and can't use ugly set+current_setting() as here ... I am using Pg9+ (9.5), so I think I can use sql-do as this in answer... But not working.
DO $$
DECLARE foo int := bar('etc');
BEGIN
SELECT run_somthing(foo,1);
SELECT run_somthing(foo,2);
END
$$;
ERROR-1: "query has no destination for the resulting data HINT: If you want to discard the results of a SELECT, use PERFORM."
ERROR-2: (after add PERFORM) "ERROR: syntax error at or near "SELECT"".
NOTE:
Also sintax error when using function workaround
CREATE or replace FUNCTION doThis() RETURNS int AS $$
DECLARE
foo int := bar('etc');
BEGIN
SELECT run_somthing(foo,1); -- bug also with PERFORM clause
SELECT run_somthing(foo,2);
RETURN 1;
END;
$$ LANGUAGE plpgsql;
SELECT doThis();

How to return multiple rows from PL/pgSQL function?

I have spent good amount of time trying to figure it out and I haven't been able to resolve it. So, I need your help please.
I am trying to write a PL/pgSQL function that returns multiple rows. The function I wrote is shown below. But it is not working.
CREATE OR REPLACE FUNCTION get_object_fields()
RETURNS SETOF RECORD
AS
$$
DECLARE result_record keyMetrics;
BEGIN
return QUERY SELECT department_id into result_record.visits
from fact_department_daily
where report_date='2013-06-07';
--return result_record;
END
$$ LANGUAGE plpgsql;
SELECT * FROM get_object_fields;
It is returning this error:
ERROR: RETURN cannot have a parameter in function returning set;
use RETURN NEXT at or near "QUERY"
After fixing the bugs #Pavel pointed out, also define your return type properly, or you have to provide a column definition list with every call.
This call:
SELECT * FROM get_object_fields()
... assumes that Postgres knows how to expand *. Since you are returning anonymous records, you get an exception:
ERROR: a column definition list is required for functions returning "record"
One way (of several) to fix this is with RETURNS TABLE (Postgres 8.4+):
CREATE OR REPLACE FUNCTION get_object_fields()
RETURNS TABLE (department_id int) AS
$func$
BEGIN
RETURN QUERY
SELECT department_id
FROM fact_department_daily
WHERE report_date = '2013-06-07';
END
$func$ LANGUAGE plpgsql;
Works for SQL functions just the same.
Related:
PostgreSQL: ERROR: 42601: a column definition list is required for functions returning "record"
I see more bugs:
first, a SET RETURNING FUNCTIONS call has following syntax
SELECT * FROM get_object_fields()
second - RETURN QUERY forwards query result to output directly. You cannot store this result to variable - it is not possible ever in PostgreSQL now.
BEGIN
RETURN QUERY SELECT ....; -- result is forwarded to output directly
RETURN; -- there will not be any next result, finish execution
END;
third - these simple functions is better to implement in SQL languages
CREATE OR REPLACE FUNCTION get_object_fields()
RETURNS SETOF RECORD AS $$
SELECT department_id WHERE ...
$$ LANGUAGE sql STABLE;
Here's one way
drop function if exists get_test_type();
drop type if exists test_comp;
drop type if exists test_type;
drop type if exists test_person;
create type test_type as (
foo int,
bar int
);
create type test_person as (
first_name text,
last_name text
);
create type test_comp as
(
prop_a test_type[],
prop_b test_person[]
);
create or replace function get_test_type()
returns test_comp
as $$
declare
a test_type[];
b test_person[];
x test_comp;
begin
a := array(
select row (m.message_id, m.message_id)
from message m
);
-- alternative 'strongly typed'
b := array[
row('Bob', 'Jones')::test_person,
row('Mike', 'Reid')::test_person
]::test_person[];
-- alternative 'loosely typed'
b := array[
row('Bob', 'Jones'),
row('Mike', 'Reid')
];
-- using a select
b := array (
select row ('Jake', 'Scott')
union all
select row ('Suraksha', 'Setty')
);
x := row(a, b);
return x;
end;
$$
language 'plpgsql' stable;
select * from get_test_type();
CREATE OR REPLACE FUNCTION get_object_fields()
RETURNS table (department_id integer)
AS
$$
DECLARE result_record keyMetrics;
BEGIN
return QUERY
SELECT department_id
from fact_department_daily
where report_date='2013-06-07';
--return result_record;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM get_object_fields()