How to dynamically select into query - sql

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;

Related

postgres - How to assign a result count to a variable and do a select query based on that

I am a from T-SQL and MS SQL Server background and struggling with PostgreSQL. I need to declare a variable, do a count query, save the result of the count in the variable; then based, on the count assign a date to another variable, and then do a select query with that assigned date to return its result set.
The problem is when I declare a variable without a DO $$ block, like so:
DECLARE num_rows bigint; I get:
ERROR: syntax error at or near "bigint"
LINE 1: DECLARE num_rows bigint;
And if I try within the DO $$ block, I get the following error on the SELECT:
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 35 at SQL statement
SQL state: 42601
This is what I am trying:
DO $$
DECLARE num_rows bigint;
DECLARE end_date timestamp with time zone;
BEGIN
SELECT COUNT(my_table.id)
INTO num_rows
FROM my_table
WHERE my_table.something = 1;
IF num_rows > 500 THEN
end_date = '2022-12-03';
END IF;
SELECT * FROM another_table WHERE some_date < end_date;
END $$;
Is there any way to accomplish this or similar in PostgreSQL? I cannot use functions because it is a legacy database and I cannot do DDL changes to it.
1)in row end_date = '2022-12-03' you need a semicolon
2)in last select statement you must use execute
I think this will work:
DO $$
DECLARE
num_rows bigint;
end_date timestamp with time zone;
BEGIN
SELECT COUNT(my_table.id)
INTO num_rows
FROM my_table
WHERE my_table.something = 1;
IF num_rows > 500 THEN
end_date = '2022-12-03';
END IF;
execute 'SELECT * FROM another_table WHERE some_date <'|| end_date;
END $$;
You can also try to run something like this:
with mydate as(
select case when (select count(*) from mytable where something = 1)>500 then '2022-12-03' end as end_date,
(select count(*) from mytable where something = 1) as num_rows
)
select * from another_table a,mydate b where a.some_date>end_date;

Syntax error while using EXECUTE format in Postgresql Function to assign variable

I have been trying to create a function that intends to assign a value to a declared variable, and act accordingly based on that value.
I use EXECUTE format(<SQL statement>) for assigning the value to cnt.
CREATE OR REPLACE FUNCTION my_function() RETURNS TRIGGER AS $$
DECLARE
cnt bigint;
BEGIN
IF NEW.field1 = 'DECLINED' THEN
cnt := EXECUTE format('SELECT count(*) FROM table1 WHERE field2 = $1 AND field1 != $2 AND id != $3;') USING NEW.field2, NEW.field1, NEW.id INTO cnt;
IF cnt = 0 THEN
EXECUTE format('UPDATE table1 SET field1 = %1$s WHERE id = $2') USING 'DECLINED', NEW.field2;
END IF;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER my_trigger BEFORE
UPDATE OF field1 ON table1 FOR EACH ROW WHEN (NEW.field1 = 'DECLINED') EXECUTE FUNCTION my_function();
However, I am getting the following error:
ERROR: syntax error at or near "("
LINE 7: cnt := EXECUTE format('SELECT count(*) FROM...
Not sure if it is relevant, but id is a text column, field1 is an ENUM, and field2 is also a text column. Could that be a problem in the SELECT statement?
Any ideas what I could be missing?
I only want to fire the second statement if cnt equals to 0
It could be rewritten as single statement:
UPDATE table1
SET field1 = ...
WHERE id = ...
AND NOT EXISTS (SELECT *
FROM table1
WHERE field2 = ...
AND field1 != ...
AND id != ...);
Using it in trigger indicates it is a try to implement partial uniqueness. If so then partial/filtered index is also an option:
CREATE UNIQUE INDEX uq ON table1(id, field1) WHERE field2 = ....;
Although #Lukasz solution may also work. I ended up using the implementation suggested by #stickybit in the comments of the question
Answer:
CREATE OR REPLACE FUNCTION my_function() RETURNS TRIGGER AS $$
DECLARE
cnt bigint;
BEGIN
IF NEW.field1 = 'DECLINED' THEN
cnt := ('SELECT count(*) FROM table1 WHERE field2 = NEW.field2 AND field1 != NEW.field1 AND id != NEW.id;')
IF cnt = 0 THEN
'UPDATE table1 SET field1 = 'DECLINED' WHERE id = NEW.field2';
END IF;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Drop table if it's empty / pass the result of count(*) to variable

I would like to check if a table is empty, and if it is, I would like drop it. I know this little function doesn't seem as a useful thing by itself, but I have a much longer function, so this is just the main part.
CREATE OR REPLACE FUNCTION public.cl_tbl(t_name character varying)
RETURNS void AS
$BODY$
DECLARE
rownum int;
BEGIN
SELECT COUNT(*) INTO rownum FROM format('myschema.%I',t_name);
IF rownum = 0 then
EXECUTE format('DROP TABLE myschema.%I',t_name);
END IF;
RETURN;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
My problem is, that the line
SELECT COUNT(*) INTO rownum FROM format('myschema.%I',t_name);
doesn't returns 0 if the table is empty, instead it returns 1 as the number of rows of the selection.
| count(bigint)
--------------------
1 | 0
I've tried this as well:
rownum := SELECT COUNT(*) FROM format('myschema.%I',t_name);
but the result is the same. How could I pass the real number of the rows of a given table?
You can use EXISTS() - SELECT EXISTS(SELECT * FROM table_name).
CREATE OR REPLACE FUNCTION public.cl_tbl(t_name character varying)
RETURNS void AS
$BODY$
DECLARE
x BOOLEAN;
BEGIN
EXECUTE format('select exists (select * from myschema.%I) t', t_name) INTO x;
IF x = False then
EXECUTE format('DROP TABLE myschema.%I',t_name);
END IF;
RETURN;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
Try using EXECUTE:
CREATE OR REPLACE FUNCTION public.cl_tbl(t_name character varying)
RETURNS void AS
$BODY$
DECLARE
rownum int;
BEGIN
EXECUTE format('select count(*) from %I', t_name) into rownum;
IF rownum = 0 then
EXECUTE format('DROP TABLE %I',t_name);
END IF;
RETURN;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
;

Debugging a PL/pgSQL function

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 ;
...

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)