Debugging a PL/pgSQL function - sql

I am trying to get this PL/pgSQL function to work :
CREATE OR REPLACE FUNCTION loopcolumns2(tableName TEXT, pourcentage real)
RETURNS void AS $$
DECLARE
_name text;
missing_percentage real;
BEGIN
FOR _name IN SELECT column_name from information_schema.columns where table_name=tableName LOOP
SELECT 100 - (count(_name) * 100) / count(*) INTO missing_percentage FROM tableName;
IF (missing_percentage > pourcentage)
THEN ALTER TABLE tableName DROP COLUMN _name;
END IF;
END LOOP;
END; $$ LANGUAGE plpgsql;
The goal of the function is to loop through all the columns of a table, a delete the columns where the percentage of missing values is bigger than an input percentage.
I get the following error :
SELECT 100 - (count( $1 ) * 100) / count(*) FROM $2
^
CONTEXT: SQL statement in PL/PgSQL function "loopcolumns2" near line 6

You're trying to SELECT from text stored by tableName vaiable. You cannot do that because Postgres think you want him to select from table named tableName, but probably such table doesn't exist.
You nead to create dynamic query in string and use EXECUTE ... INTO ... statement. Like this:
DECLARE query TEXT;
...
query := 'SELECT 100 - (count(' || _name::TEXT || ') * 100) / count(*) FROM '
|| tableName::TEXT;
EXECUTE query INTO percentage ;
...

Related

run a query saved in a row value

The following select statement gives me three cols and one row.
QueryA
SELECT
-- some calculations and manipulations
x, y, queryB
.....
FROM INFORMATION_SCHEMA.COLUMNS c
WHERE TABLE_SCHEMA = UPPER($sch_name)
AND TABLE_NAME = UPPER($tab_name);
One of the returned columns has a query stored. Output
x, y, queryB
aa bb select * from stg.new
How can I extend the QueryA such that I select and run the query B and the final output returned is that of queryB not queryA?
Here is a sample of reading from one table to get the SQL, executing the SQL on a different table, and then returning the result of that second statement:
create or replace transient table T1 as
select 'This is the final result.' as THE_RESULT
;
create or replace transient table SQL_TO_RUN as
select 'select * from T1' SQL_STATEMENT
;
create or replace procedure myprocedure()
returns table()
language sql
execute as caller
as
$$
declare
rs resultset default (select SQL_STATEMENT from SQL_TO_RUN);
c cursor for rs;
sqlStatement string;
rsFinal resultset;
begin
for rowContents in c do
sqlStatement := rowContents.SQL_STATEMENT;
end for;
rsFinal := (execute immediate :sqlStatement);
return table(rsFinal);
end;
$$
;
call myprocedure();

Include parameter in function to UNION multiple PostgreSQL tables

I have developed a function to UNION ALL tables from a list of table names (a table called tablelist below) inspired by this SO post.
The initial function just returns a selection, but now I'd like to write a new table with a name taken from a parameter new_table_name.
I'm struggling with the syntax to insert the parameter into the DROP TABLE AND CREATE TABLE statements. Here's one of the attempts which returns ERROR: mismatched parentheses at or near ";"
DROP FUNCTION IF EXISTS f_multi_union(text);
CREATE OR REPLACE FUNCTION f_multi_union(new_tab_name text)
RETURNS Table (my_id int, metric double precision, geom geometry)
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY EXECUTE
(
DROP TABLE IF EXISTS working.'' || new_tab_name || '';
CREATE TABLE working.'' || new_tab_name || '' AS (
SELECT string_agg(format('SELECT * FROM %s', tbl), ' UNION ALL ')
FROM (SELECT tbl FROM working.tablelist) sub
)
);
END
$func$;
Something like this?
DROP FUNCTION IF EXISTS f_multi_union(text);
CREATE OR REPLACE FUNCTION f_multi_union(new_tab_name text)
RETURNS void -- nothing to return
LANGUAGE plpgsql AS
$func$
DECLARE
_sql TEXT;
BEGIN
_sql := format('DROP TABLE IF EXISTS working.%I;', new_tab_name); -- avoid SQL injection
EXECUTE _sql;
_sql := 'SELECT string_agg(format(''SELECT * FROM %I'', tbl), '' UNION ALL '')
FROM (SELECT tbl FROM working.tablelist) sub;';
EXECUTE _sql
INTO _sql; -- overwrite current _sql content
_sql := format('CREATE TABLE working.%I AS %s;', new_tab_name, _sql);
EXECUTE _sql;
END
$func$;
I would replace the * in the SELECT statement with the columns that you need.

How to use string with apostrophe in function variable plpgsql

Hello I am having trouble querying when I have apostrophe in my where clause in postgresql using pgpsql function, I know that manually I could do something like:
select 'author''s'
however my word is stored in a variable, here is my function:
CREATE OR REPLACE FUNCTION public.fn_inserir_doc(caminho_arqv text, conteudo text)
RETURNS void
LANGUAGE plpgsql
AS $function$
declare
conteudo_array text array;
palavra text;
begin
execute 'insert into documento(caminho)
select ''' || caminho_arqv || '''
where not exists(select id
from documento
where caminho='''||caminho_arqv||''')';
conteudo_array := regexp_split_to_array(conteudo, E'\\s+');
FOREACH palavra in array conteudo_array
loop
if length(palavra) >=3 then
raise notice 'palavra: %', palavra;
execute 'insert into termo(descricao)
select ''' || palavra || '''
where not exists(
select id from termo
where descricao='''||palavra||''')';
execute 'insert into documento_termo(id_termo, id_documento, frequencia)
select t.id, d.id, 1
from termo t
cross join documento d
where t.descricao = '''|| palavra ||'''
and d.caminho = '''|| caminho_arqv ||'''
on conflict (id_termo, id_documento) do update set frequencia = documento_termo.frequencia + 1;';
end if;
end loop;
end;
$function$
The following sample is the one that has the problem:
select id from termo
where descricao='''||palavra||'''
because palavra contains single quote
Use dollar quoting and the function format(). Example:
create or replace function test(str text)
returns setof text language plpgsql as $$
begin
-- instead of this:
-- return query execute 'select '''||str||'''::text';
-- use:
return query execute format(
$fmt$
select %L::text
$fmt$, str);
end $$;
select * from test('O''Brian');
test
---------
O'Brian
(1 row)

How to dynamically select into query

EXECUTE format(
'SELECT MAX(sum)
FROM table 1
WHERE %s = %s') INTO max_value USING id_column, id_value;
Getting error: ERROR: operator does not exist: character varying =
integer.
Also, when I try to run the below code:
update table1 set column2 = max_value + sum;
I get another error:
"ERROR: column "max_value" does not exist"
You can use this:
CREATE OR REPLACE FUNCTION maxvaloftable ()
RETURNS integer AS
$BODY$
DECLARE
resultat integer = 2;
BEGIN
resultat= (SELECT MAX(sum) FROM table) ;
RETURN resultat;
END;
$BODY$
LANGUAGE plpgsql VOLATILE STRICT
COST 100;
ALTER FUNCTION maxvaloftable()
OWNER TO postgres;
update table1 set column2 = (select maxvaloftable ()) + sum;

Extract specific Columns from PostgreSQL table and Do an update on its values

I have a PostgreSQL database and I need to do an update over values of specific Columns. The number of columns is so big and I need to do the same operation to different table So better to extract them dynamically.
More specifically I want to extract from the table all the columns whose names ends with "_suffix" and do an update on their values.
I started trying to make a script but I don't know if it is the right road!
SELECT columns.column_name
FROM information_schema.columns
WHERE columns.table_name = 'myInitialTable' AND columns.column_name like '%\_suffix%' AND columns.table_schema = 'public';
I created a view of this query and I used it in the following function :
CREATE OR REPLACE FUNCTION updatetable() RETURNS int4 AS
$BODY$
DECLARE r RECORD;
BEGIN
FOR r IN SELECT * from v_reduced_table LOOP
update myInitialTable
set r.column_name = case
when r.column_name = '' then NULL
when r.column_name = 'value1' or r.column_name = 'value2' then 'xxxxx'
else r.column_name end;
END LOOP;
return 1;
END;
$BODY$
LANGUAGE plpgsql;
SELECT updatetable() as output;
this query do a loop on every column ending with suffix and updates its values. but when I run it I get
ERROR: syntax error at or near "$1"
LINE 1: update myInitialTable set $1 = case when $2 = '' then NULL when ...
Any help is appreciated :)
In your function you need to use dynamic commands.
The funcion format() is often very helpful.
Example data:
create table my_table(col1_suffix text, col2_suffix text, col3_suffix text);
insert into my_table values ('a', 'b', 'c');
Example function:
CREATE OR REPLACE FUNCTION update_my_table() RETURNS void AS
$BODY$
DECLARE r RECORD;
BEGIN
FOR r IN
SELECT columns.column_name
FROM information_schema.columns
WHERE columns.table_name = 'my_table'
AND columns.column_name like '%\_suffix%'
AND columns.table_schema = 'public'
LOOP
EXECUTE(FORMAT($f$
UPDATE my_table
SET %s = CASE
WHEN '%s' = 'col1_suffix' THEN 'col1'
WHEN '%s' = 'col2_suffix' OR '%s' = 'col3_suffix' THEN 'xxxxx'
END;$f$, r.column_name, r.column_name, r.column_name, r.column_name));
END LOOP;
END;
$BODY$
LANGUAGE plpgsql;
Usage:
select update_my_table();
select * from my_table;
col1_suffix | col2_suffix | col3_suffix
-------------+-------------+-------------
col1 | xxxxx | xxxxx
(1 row)