Unable to run a function with a select statement - sql

I want to build a sql function that returns array of rows based on a select statement, unfortunately I couldn't make it work.
create or replace function latestOilFee()
return array
IS
Begin
select * from oil_tracker order by ts desc fetch first 1 row only;
End;
console throws an error
syntax error at or near "return"

There are multiple mistakes
RETURNS instead of RETURN
For ARRAY you have to mentioned like text[]
CREATE OR REPLACE FUNCTION latestOilFee()
RETURNS text[] AS $$
select array[fieldname::text, fieldname::text]
FROM oil_tracker limit 1
$$
LANGUAGE SQL;
Please check above, you should mentioned fields type while creating array

Shouldn't it be RETURNS (not RETURN)?

Related

How to pass variable from PL/pgSQL function to a query select statement?

CREATE OR REPLACE FUNCTION data_of(_tbl_type anyelement, _missing_id TEXT)
RETURNS SETOF anyelement AS
$func$
BEGIN
RETURN QUERY EXECUTE format('
SELECT *
from logs
where json_col_a::text like '%$1%' or -- <---- faulty
json_col_b::text like '%$1%' --- <----- faulty
order by timestamp desc'
, pg_typeof(_tbl_type))
USING _missing_id;
END
$func$ LANGUAGE plpgsql;
CREATE TABLE new_table AS TABLE logs WITH NO DATA;
SELECT * FROM data_of(NULL::new_table, '00000000-0000-0000-0000-000000000001');
Adapted from the answer here, I want to pass a GUID as a text into the SELECT query.
I convert the json_col_a, json_col_b from json to text.
Then, I check whether the _missing_id text is found in both columns above.
When I run, I got an error message saying:
ERROR: operator does not exist: unknown % new_table
LINE 4: where diagnostics_context::text like '%$1%' or
What have I missed?
Thanks

PostgreSQL: migrating ms sql xml query to postgresql query

I am trying to migrate the following MS SQL Server procedure to a PostgreSQL function.
CREATE PROCEDURE [dbo].[GMC]
AS
BEGIN
DECLARE #LID VARCHAR(3);
DECLARE #xml XML = '<XMLProf><CID>840</CID><MD>101113</MD></XMLProf>';
SELECT #LID = Pay.b.value('.','Varchar(3)')
FROM #xml.nodes('/XMLProf/CID') as Pay(b)
SELECT 'Return Value' = #LID
END
I have tried to convert to the following but it doesn't work.
CREATE OR REPLACE FUNCTION dbo.GMC()
RETURNS void
AS
$BODY$
DECLARE
LID VARCHAR(3);
bxml XML = '<XMLProf><CID>840</CID><MD>101113</MD></XMLProf>';
BEGIN
SELECT LID = Pay.b.value('.','Varchar(3)')
FROM XMLTABLE('/XMLProf/CID' PASSING bxml) as Pay(b)
SELECT 'Return Value' = LID
end;
$BODY$
LANGUAGE plpgsql;
Edit:
The result I am expecting is "840"
The error that I am getting is a syntax error:
ERROR: syntax error at or near ")"
LINE 12: FROM XMLTABLE('/XMLProf/CID' PASSING bxml) as Pay(b)
Can someone please tell me how can I accomplish this. Any help is really appreciated.
If you want to return something from a function, you can't use returns void. As XML is character data, returns text makes more sense.
As you only want to return a single value, xmltable() isn't really needed. And you don't need PL/pgSQL either:
CREATE OR REPLACE FUNCTION dbo.gmc()
RETURNS text
AS
$BODY$
select (xpath('/XMLProf/CID/text()',
'<XMLProf><CID>840</CID><MD>101113</MD></XMLProf>'::xml))[1]::text;
$BODY$
LANGUAGE sql;
xpath() returns an array of all matches, that's why the [1] is needed to pick out the first match.
Assuming you actually want to pass the XML to the function, you can use this:
CREATE OR REPLACE FUNCTION dbo.gmc(p_xml xml)
RETURNS text
AS
$BODY$
select (xpath('/XMLProf/CID/text()', p_xml))[1]::text;
$BODY$
LANGUAGE sql;
If you're basing your response element on the string size of the inner nodes of XMLProf, you might wanna take a look at XPATH and UNNEST.
CREATE OR REPLACE FUNCTION gmc() RETURNS text
AS $BODY$
WITH j AS (
SELECT
UNNEST(XPATH('//XMLProf/node()',
'<XMLProf><CID>840</CID><MD>101113</MD></XMLProf>'::XML)) AS rawxml
) SELECT (XPATH('//text()',j.rawxml))[1]::TEXT FROM j
WHERE CHAR_LENGTH((XPATH('//text()',j.rawxml))[1]::TEXT) = 3
$BODY$
LANGUAGE sql;
Testing ..
db=# SELECT * FROM gmc();
gmc
-----
840
(1 Zeile)
If you know exactly where to look and the string length is irrelevant, just get rid of the UNNEST and use the XPATH /XMLProf/CID/text() as pointed out by #a_horse_with_no_name.

"ERROR: query has no destination for result data"

I am trying to create a function like this:
I tried changing the return type to int or text etc. to see if the code works outside of that, but it doesn't. I am a beginner in PostgreSQL so please don't be harsh if I missed something obvious.
create or replace function date_select(i INT) returns void as
$$
begin
select * from dwh_stg.stg_dce_gift where gift_id = i;
end
$$ language plpgsql
select date_select(16940)
SQL Error [42601]:
ERROR: query has no destination for result data
Hint: If you want to discard the results of a SELECT, use PERFORM instead.
Where: PL/pgSQL function date_select(integer) line 3 at SQL statement
If you want to return something, you need to define the function to return something (not void)
Apparently you want to return multiple rows from the table stg_dec_gift, for that you need to define the function as returns setof dwh_stg.stg_dce_gift. For a simple function encapsulating a query there is not need to use PL/pgSQL a plain SQL function will do just fine:
create or replace function date_select(i INT)
returns setof dwh_stg.stg_dce_gift --<< here
as
$$
select *
from dwh_stg.stg_dce_gift
where gift_id = i;
$$
stable
language sql;
Then use it in the FROM part:
select *
from date_select(16940);
Online example: https://rextester.com/WYDCE44062

Unable to use SET OF record or return table(col1,[col2]) return type in pgadmin 4 version 2?

Basically i want to split the string based on the delimiter for example :-
"this,is,foo" should return this is foo i.e; without the comma into separate records .
i am trying to use pgadmin 4 version 2.
this is my sample code below that i want in function dialog :-
create or replace function STRING_SPLIT(str text,delimiter character)
return SET OF record;
as
begin
return QUERY SELECT trim(a)
FROM unnest(string_to_array('john,smith,jones', ',')) AS a; //str, delim
end
i am basically trying to insert the expanded row array into set of records.
i tried creating table and return it in the return statement but could not specify the column name in function dialog of pgadmin also i could not find "set of record" return type in the same dialog box?
can anyone help?
In almost all cases, using returns table() is the better solution.
create or replace function STRING_SPLIT(str text,delimiter character)
return table(word text) --<< no ; here!!
as
$$ --<< you need quotes around the body
SELECT trim(a) as word
FROM unnest(string_to_array(text, delimiter)) AS t(a);
$$
language sql; --<< no PL/pgSQL required, SQL is enough
If you do want to use setof record then you always have to specify the column names when using the function:
create or replace function STRING_SPLIT(str text,delimiter character)
return setof record
as
$$ --<< you need quotes around the body
SELECT trim(a) as word
FROM unnest(string_to_array(text, delimiter)) AS t(a);
$$
language sql; --<< no PL/pgSQL required, SQL is enough
Then:
select *
from string_split('foo,bar',',') as t(word);
However, Postgres already has a built-in function for this:
select *
from regexp_split_to_table('foo,bar',',') as t(word);
Though this post is older, but needs to post an answer here. So it will help people like me. As I was looking for the same and found the answer.
in pgAdmin4 'setof' is not present in the return type. While creating function in pgAdmin using create function window, you need to select record as a return type.
Then click on the options and set "Returns a set?" to "Yes". like this
Then it will automatically take setof record as a return type and will show in code in sql viwer, but not in return type selected.
Then it will automatically take setof record as a return type and will show in code in sql viwer, but not in return type selected.

Input table for PL/pgSQL function

I would like to use a plpgsql function with a table and several columns as input parameter. The idea is to split the table in chunks and do something with each part.
I tried the following function:
CREATE OR REPLACE FUNCTION my_func(Integer)
RETURNS SETOF my_part
AS $$
DECLARE
out my_part;
BEGIN
FOR i IN 0..$1 LOOP
FOR out IN
SELECT * FROM my_func2(SELECT * FROM table1 WHERE id = i)
LOOP
RETURN NEXT out;
END LOOP;
END LOOP;
RETURN;
END;
$$
LANGUAGE plpgsql;
my_func2() is the function that does some work on each smaller part.
CREATE or REPLACE FUNCTION my_func2(table1)
RETURNS SETOF my_part2 AS
$$
BEGIN
RETURN QUERY
SELECT * FROM table1;
END
$$
LANGUAGE plpgsql;
If I run:
SELECT * FROM my_func(99);
I guess I should receive the first 99 IDs processed for each id.
But it says there is an error for the following line:
SELECT * FROM my_func2(select * from table1 where id = i)
The error is:
The subquery is only allowed to return one column
Why does this happen? Is there an easy way to fix this?
There are multiple misconceptions here. Study the basics before you try advanced magic.
Postgres does not have "table variables". You can only pass 1 column or row at a time to a function. Use a temporary table or a refcursor (like commented by #Daniel) to pass a whole table. The syntax is invalid in multiple places, so it's unclear whether that's what you are actually trying.
Even if it is: it would probably be better to process one row at a time or rethink your approach and use a set-based operation (plain SQL) instead of passing cursors.
The data types my_part and my_part2 are undefined in your question. May be a shortcoming of the question or a problem in the test case.
You seem to expect that the table name table1 in the function body of my_func2() refers to the function parameter of the same (type!) name, but this is fundamentally wrong in at least two ways:
You can only pass values. A table name is an identifier, not a value. You would need to build a query string dynamically and execute it with EXECUTE in a plpgsql function. Try a search, many related answers her on SO. Then again, that may also not be what you wanted.
table1 in CREATE or REPLACE FUNCTION my_func2(table1) is a type name, not a parameter name. It means your function expects a value of the type table1. Obviously, you have a table of the same name, so it's supposed to be the associated row type.
The RETURN type of my_func2() must match what you actually return. Since you are returning SELECT * FROM table1, make that RETURNS SETOF table1.
It can just be a simple SQL function.
All of that put together:
CREATE or REPLACE FUNCTION my_func2(_row table1)
RETURNS SETOF table1 AS
'SELECT ($1).*' LANGUAGE sql;
Note the parentheses, which are essential for decomposing a row type. Per documentation:
The parentheses are required here to show that compositecol is a column name not a table name
But there is more ...
Don't use out as variable name, it's a keyword of the CREATE FUNCTION statement.
The syntax of your main query my_func() is more like psudo-code. Too much doesn't add up.
Proof of concept
Demo table:
CREATE TABLE table1(table1_id serial PRIMARY KEY, txt text);
INSERT INTO table1(txt) VALUES ('a'),('b'),('c'),('d'),('e'),('f'),('g');
Helper function:
CREATE or REPLACE FUNCTION my_func2(_row table1)
RETURNS SETOF table1 AS
'SELECT ($1).*' LANGUAGE sql;
Main function:
CREATE OR REPLACE FUNCTION my_func(int)
RETURNS SETOF table1 AS
$func$
DECLARE
rec table1;
BEGIN
FOR i IN 0..$1 LOOP
FOR rec IN
SELECT * FROM table1 WHERE table1_id = i
LOOP
RETURN QUERY
SELECT * FROM my_func2(rec);
END LOOP;
END LOOP;
END
$func$ LANGUAGE plpgsql;
Call:
SELECT * FROM my_func(99);
SQL Fiddle.
But it's really just a a proof of concept. Nothing useful, yet.
As the error log is telling you.. you can return only one column in a subquery, so you have to change it to
SELECT my_func2(SELECT Specific_column_you_need FROM hasval WHERE wid = i)
a possible solution can be that you pass to funct2 the primary key of the table your funct2 needs and then you can obtain the whole table by making the SELECT * inside the function