PostgreSQL 9.3: Return strings from function - postgresql

I have the following function called as pro(). From which I want to return the strings by union all of two select statements and product output.
Function: pro()
My try:
create or replace function pro()
returns varchar as
$$
declare
sql varchar;
q varchar;
begin
sql := 'SELECT DISTINCT CAST(COUNT(ProductNumber) as varchar) ||'' - Count of the product Number '' as Descp
FROM product
UNION ALL
SELECT DISTINCT CAST(COUNT(ProductName) AS varchar) || '' - Count of the product Name '' as Descp
FROM product';
raise info '%',sql;
execute sql into q;
return q;
end;
$$
language plpgsql;
Calling Function:
select pro();
This returning only the first part of select statement:
______________________________________
|pro |
|character varying |
|______________________________________|
|6 - Count of the product Number |
|______________________________________|
But the expected result should be:
______________________________________
|pro |
|character varying |
|______________________________________|
|6 - Count of the product Number |
|______________________________________|
|6 - Count of the product Name |
|______________________________________|

Try use these functions :
using plpgsql
create or replace function pro1()returns
table (
descp text
)
as
$$
begin
return QUERY execute (
'SELECT DISTINCT CAST(COUNT(product) as varchar) ||'' - Count of the product Number '' as Descp
FROM product
UNION ALL
SELECT DISTINCT CAST(COUNT(productid) AS varchar) || '' - Count of the product Name '' as Descp
FROM product');
end;
$$
language plpgsql;
or
using sql
create or replace function pro2() returns table ( descp text)
as
$$
SELECT DISTINCT CAST(COUNT(product) as varchar) ||' - Count of the product Number ' as Descp
FROM product
UNION ALL
SELECT DISTINCT CAST(COUNT(productid) AS varchar) || ' - Count of the product Name 'as Descp
FROM product;
$$
language sql;

Related

Count for each columns in table (not null) PostgreSQL PL/pgSQL

I am trying to count the number of rows that do not contain null for each column in the table
There is a simple table actor_new
The first 2 columns (actor_id, first_name) contain 203 rows not null
Other 2 columns (last_name, last_update) contain 200 rows not null
This is a simple test that outputs the same value for all columns, but if you perform select separately, then everything works correctly, please help me understand the LOOP block
create or replace function new_cnt_test_ho(in_table text, out out_table text, out cnt_rows int) returns setof record AS $$
DECLARE i text;
BEGIN
FOR i IN
select column_name
from information_schema."columns"
where table_schema = 'public'
and table_name = in_table
LOOP
execute '
select $1, count($1)
from '|| quote_ident(in_table) ||'
where $1 is not null '
INTO out_table, cnt_rows
using i, quote_literal(i), quote_ident(in_table), quote_literal(in_table) ;
return next;
END LOOP;
END;
$$LANGUAGE plpgsql
Result:
select * from new_cnt_test_ho('actor_new')
out_table |cnt_rows|
-----------+--------+
actor_id | 203|
first_name | 203|
last_name | 203|
last_update| 203|
There are 4 parameters specified in using, because I assumed that the error was in quotes, I took turns playing with arguments from 1 to 4
The correct result should be like this
out_table |cnt_rows|
-----------+--------+
actor_id | 203|
first_name | 203|
last_name | 200|
last_update| 200|
based on your title: input is a table name, output is a table one column is column name, another column is return of count(column)
first check the table exists or not.
then for loop get each column name, after that for each column name run a query.
a sample query is select 'cola',count(cola) from count_nulls. first occurrence is literal 'cola', so we need quote_literal(cols.column_name),
second is the column name, so we need use quote_ident(cols.column_name)
select 'cola',count(cola) from count_nulls will count column cola all not null value. if a column all value is null then return 0.
The following function will return the expected result. Can be simplified, since i use a lot of raise notice.
CREATE OR REPLACE FUNCTION get_all_nulls (_table text)
RETURNS TABLE (
column_name_ text,
numberofnull bigint
)
AS $body$
DECLARE
cols RECORD;
_sql text;
_table_exists boolean;
_table_reg regclass;
BEGIN
_table_reg := _table::regclass;
_table_exists := (
SELECT
EXISTS (
SELECT
FROM
pg_tables
WHERE
schemaname = 'public'
AND tablename = _table));
FOR cols IN
SELECT
column_name
FROM
information_schema.columns
WHERE
table_name = _table
AND table_schema = 'public' LOOP
_sql := 'select ' || quote_literal(cols.column_name) || ',count(' || quote_ident(cols.column_name) || ') from ' || quote_ident(_table::text);
RAISE NOTICE '_sql:%', _sql;
RETURN query EXECUTE _sql;
END LOOP;
END;
$body$ STRICT
LANGUAGE plpgsql;
setup.
begin;
create table count_nulls(cola int, colb int, colc int);
INSERT into count_nulls values(null,null,null);
INSERT into count_nulls values(1,null,null);
INSERT into count_nulls values(2,3,null);
commit;

How to return COUNT of function1's results in function2

I have a function, let's call it get_pants, which returns the following in psql:
select * from get_pants ('calvin klein');
size | length | color | type | price | discount | inventory | creation_timestamp
-----+----------+------------+-------------+-------------+-------------+---------+--------------------
(0 rows)
I can count the results of get_pants in psql also, like so
select count(*) from get_pants ('calvin klein');
count
-------
0
(1 row)
But when I put the same code into a function, let's call it foo, like below, why do I get a syntax error
create or replace function foo ()
returns bigint as $$
begin
return
select count(*) from get_pants ('calvin klein');
end
$$ language 'plpgsql';
syntax error at or near "select"
LINE 4: return select count(*) from get_pants ('calvin.kl...
^
In PL/pgSQL you need to first store the result in a variable:
create or replace function foo()
returns bigint
as $$
declare
l_count bigint;
begin
select count(*)
into l_result
from get_pants ('calvin klein');
return l_result;
end
$$
language plpgsql
stable;
However you don't need PL/pgSQL for this:
create or replace function foo()
returns bigint
as $$
select count(*)
from get_pants ('calvin klein');
$$
language sql
stable;
Note that the language name is an identifier and should not be enclosed in single quotes. The syntax is deprecated and might not be accepted in a future version.

Array error passing dynamic number of parameters to function

I'm trying to create a function to receive the name of the table in my schema already created and a several name of columns within this table (dynamic number of columns) and return a table with all the columns in a unique column with the value of each column separated by comma.
I'm trying this:
CREATE OR REPLACE PROCEDURE public.matching(IN table text, VARIADIC column_names text[])
LANGUAGE 'plpgsql'
AS $BODY$DECLARE
column_text text;
BEGIN
EXECUTE format ($$ SELECT array_to_string(%s, ' ')$$, column_names) into column_text;
EXECUTE format ($$ CREATE TABLE temp1 AS
SELECT concat(%s, ' ') FROM %s $$, column_text, table);
END;$BODY$;
This return an error:
ERROR: syntax error at or near «{»
LINE 1: SELECT array_to_string({city,address}, ' ')
which is the error?
If you simplify the generation of the dynamic SQL, things get easier:
CREATE OR REPLACE PROCEDURE public.matching(IN table_name text, VARIADIC column_names text[])
LANGUAGE plpgsql
AS
$BODY$
DECLARE
l_sql text;
BEGIN
l_sql := format($s$
create table temp1 as
select concat_ws(',', %s) as everything
from %I
$s$, array_to_string(column_names, ','), table_name);
raise notice 'Running %', l_sql;
EXECUTE l_sql;
END;
$BODY$;
So if you e.g. pass in 'some_table' and {'one', 'two', 'three'} the generated SQL will look like this:
create table temp1 as select concat_ws(',', one,two,three) as everything from some_table
I also used a column alias for the new column, so that the new table has a defined name. Note that the way I put the column names into the SQL string won't properly deal with identifiers that need double quotes (but they should be avoided anyway)
If you want to "return a table", then maybe a function might be the better solution:
CREATE OR REPLACE function matching(IN table_name text, VARIADIC column_names text[])
returns table (everything text)
LANGUAGE plpgsql
AS
$BODY$
DECLARE
l_sql text;
BEGIN
l_sql := format($s$
select concat_ws(',', %s) as everything
from %I
$s$, array_to_string(column_names, ','), table_name);
return query execute l_sql;
END;
$BODY$;
Then you can use it like this:
select *
from matching('some_table', 'one', 'two', 'three');
I propose different but similar code.
With following script:
CREATE OR REPLACE PROCEDURE public.test(IN p_old_table text, IN p_old_column_names text[], IN p_new_table text)
LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
old_column_list text;
ctas_stmt text;
BEGIN
old_column_list = array_to_string(p_old_column_names,',');
RAISE NOTICE 'old_column_list=%', old_column_list;
ctas_stmt = format('CREATE TABLE %s AS SELECT %s from %s', p_new_table, old_column_list, p_old_table);
RAISE NOTICE 'ctas_stmt=%', ctas_stmt;
EXECUTE ctas_stmt;
END;
$BODY$;
--
create table t(x int, y text, z timestamp, z1 text);
insert into t values (1, 'OK', current_timestamp, null);
select * from t;
--
call test('t',ARRAY['x','y','z'], 'tmp');
--
\d tmp;
select * from tmp;
I have following execution:
CREATE OR REPLACE PROCEDURE public.test(IN p_old_table text, IN p_old_column_names text[], IN p_new_table text)
LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
old_column_list text;
ctas_stmt text;
BEGIN
old_column_list = array_to_string(p_old_column_names,',');
RAISE NOTICE 'old_column_list=%', old_column_list;
ctas_stmt = format('CREATE TABLE %s AS SELECT %s from %s', p_new_table, old_column_list, p_old_table);
RAISE NOTICE 'ctas_stmt=%', ctas_stmt;
EXECUTE ctas_stmt;
END;
$BODY$;
CREATE PROCEDURE
create table t(x int, y text, z timestamp, z1 text);
CREATE TABLE
insert into t values (1, 'OK', current_timestamp, null);
INSERT 0 1
select * from t;
x | y | z | z1
---+----+----------------------------+----
1 | OK | 2020-04-14 11:37:28.641328 |
(1 row)
call test('t',ARRAY['x','y','z'], 'tmp');
psql:tvar.sql:24: NOTICE: old_column_list=x,y,z
psql:tvar.sql:24: NOTICE: ctas_stmt=CREATE TABLE tmp AS SELECT x,y,z from t
CALL
Table "public.tmp"
Column | Type | Collation | Nullable | Default
--------+-----------------------------+-----------+----------+---------
x | integer | | |
y | text | | |
z | timestamp without time zone | | |
select * from tmp;
x | y | z
---+----+----------------------------
1 | OK | 2020-04-14 11:37:28.641328
(1 row)

How to do postgresql select query funciton using parameter?

I want to create a postgresql funciton that returns records. But if I pass an id parameter, it should be add in where clause. if I do not pass or null id parameter, where clasuse will not add the query.
CREATE OR REPLACE FUNCTION my_func(id integer)
RETURNS TABLE (type varchar, total bigint) AS $$
DECLARE where_clause VARCHAR(200);
BEGIN
IF id IS NOT NULL THEN
where_clause = ' group_id= ' || id;
END IF ;
RETURN QUERY SELECT
type,
count(*) AS total
FROM
table1
WHERE
where_clause ???
GROUP BY
type
ORDER BY
type;
END
$$
LANGUAGE plpgsql;
You can either use one condition that takes care of both situations (then you don't need PL/pgSQL to begin with):
CREATE OR REPLACE FUNCTION my_func(p_id integer)
RETURNS TABLE (type varchar, total bigint)
AS $$
SELECT type,
count(*) AS total
FROM table1
WHERE p_id is null or group_id = p_id
GROUP BY type
ORDER BY type;
$$
LANGUAGE sql;
But an OR condition like that is typically not really good for performance. The second option you have, is to simply run two different statements:
CREATE OR REPLACE FUNCTION my_func(p_id integer)
RETURNS TABLE (type varchar, total bigint)
AS $$
begin
if (p_id is null) then
return query
SELECT type,
count(*) AS total
FROM table1
GROUP BY type
ORDER BY type;
else
return query
SELECT type,
count(*) AS total
FROM table1
WHERE group_id = p_id
GROUP BY type
ORDER BY type;
end if;
END
$$
LANGUAGE plgpsql;
And finally you can build a dynamic SQL string depending the parameter:
CREATE OR REPLACE FUNCTION my_func(p_id integer)
RETURNS TABLE (type varchar, total bigint)
AS $$
declare
l_sql text;
begin
l_sql := 'SELECT type, count(*) AS total FROM table1 '
if (p_id is not null) then
l_sql := l_sql || ' WHERE group_id = '||p_id;
end if;
l_sql := l_sql || ' GROUP BY type ORDER BY type';
return query execute l_sql;
end;
$$
LANGUAGE plpgsql;
Nothing is required just to use the variable as it is for more info please refer :plpgsql function parameters

Using variables in a plpgsql function

Ok so I used a string_agg like this.
select string_agg(DISTINCT first_name,', ' ORDER BY first_name) FROM person_test;
Then I wrote this to return the values to a table.
SELECT *
FROM person_test
where first_name = ANY(string_to_array('Aaron,Anne', ','));
Now I want to put this in a function so that instead of acturally putting names into the string_to_array, I can just call the string_agg.
I am new to postgres and am not finding any good documentation on how to do this online. I believe I would have to declare the the string_agg and then call it in string_to_array but I am having no such luck.
This was my attempt, I know this is now right but if anyone could add some feedback. I am getting an error between results and ALAIS and on the return.
create or REPLACE FUNCTION select_persons(VARIADIC names TEXT[]);
declare results ALIAS select string_agg(DISTINCT first_name,', ' ORDER BY first_name) FROM person_test;
BEGIN
return setof person_test LANGUAGE sql as $$
select * from person_test
where first_name = any(results)
end;
$$ language sql;
You can create a function with variable number of arguments.
Example:
create table person_test (id int, first_name text);
insert into person_test values
(1, 'Ann'), (2, 'Bob'), (3, 'Ben');
create or replace function select_persons(variadic names text[])
returns setof person_test language sql as $$
select *
from person_test
where first_name = any(names)
$$;
select * from select_persons('Ann');
id | first_name
----+------------
1 | Ann
(1 row)
select * from select_persons('Ann', 'Ben', 'Bob');
id | first_name
----+------------
1 | Ann
2 | Bob
3 | Ben
(3 rows)
To use a variable inside a plpgsql function, you should declare the variable and use select ... into (or assignment statement). Example:
create or replace function my_func()
returns setof person_test
language plpgsql as $$
declare
aggregated_names text;
begin
select string_agg(distinct first_name,', ' order by first_name)
into aggregated_names
from person_test;
-- here you can do something using aggregated_names
return query
select *
from person_test
where first_name = any(string_to_array(aggregated_names, ', '));
end $$;
select * from my_func();