Creating TEMP TABLE dynamically in Postgresql and selecting the same table in FOR loop. But getting the error near PIPE symbol - postgresql

do
$xyz$
declare
y text;
i record;
begin
y := to_char(current_timestamp, 'YYYYMMDDHHMMSS');
raise notice '%',y;
execute 'CREATE TEMP TABLE someNewTable'
||y
||' AS select * from ( VALUES(0::int,-99999::numeric), (1::int, 100::numeric)) as t (key, value)';
for i in (select * from someNewTable||y) loop
raise notice '%',i.key;
end loop;
end;
$xyz$ language 'plpgsql'
ERROR: syntax error at or near "||"
LINE 13: for i in (select * from someNewTable||y) loop
Im unable to understand why the error is at the PIPE symbol. Please help me. I have been trying in Oracle db too, but same error. Am I doing anything wrong here?

The query in for ... loop statement also has to be dynamic, so you should use execute twice.
Use the format() function which is very convenient in conjunction with execute:
do $xyz$
declare
y text;
i record;
begin
y := to_char(current_timestamp, 'YYYYMMDDHHMMSS');
raise notice '%', y;
execute format($ex$
create temp table somenewtable%s
as select * from (
values
(0::int, -99999::numeric),
(1::int, 100::numeric)
) as t (key, value)
$ex$, y);
for i in
execute format($ex$
select * from somenewtable%s
$ex$, y)
loop
raise notice '%',i.key;
end loop;
end;
$xyz$ language 'plpgsql';

Related

PostgreSql procedure structure

I have a syntax error on a procedure (PostgreSQL) and I can't find the mistake. I need a procedure with the variable declaration, a for loop, and a transaction.
Here is a sample of the structure I'm using.
CREATE OR REPLACE PROCEDURE repor_dados()
LANGUAGE plpgsql AS
$$
DECLARE
utilizador int;
data_acesso date;
r record;
BEGIN
BEGIN -- begin transaction
select 2 ,4 into data_acesso,utilizador;
IF EXISTS(select 2) then -- condition
BEGIN
-- for each select result
FOR r IN SELECT 1,2,3
LOOP
-- do something
IF r = 1 THEN
SELECT 3;
ELSE
SELECT 4;
END IF;
END LOOP;
END;
COMMIT; -- commit transaction
END
$$
I am working with PostgreSQL 13.2.
The error is:
ERROR: syntax error at end of input LINE 30: $$

How to implement execute command inside for loop - postgres, dbeaver

I am new to postgres. I need to create a function that will take a list of all the tables in the database whose names are stored in one table and then delete the records of all the tables that are older than x days and have a certain row_status. Some tables do not have a row_status column.
I get an error when I try to save a written function in dbeaver -> ERROR: syntax error at or near "||"
create function delete_old_records1(day1 int, row_status1 character default null, row_status2 character default null)
returns void
language plpgsql
as $$
declare
c all_tables1%rowtype;
begin
for c in select * from all_tables1 loop
if exists(SELECT column_name FROM information_schema.columns
WHERE table_schema = 'yard_kondor' AND table_name =c.table_name AND column_name = 'row_status') then
execute 'delete from '||c.table_name||' where row_create_datetime>current_date-day1+1 and
row_status in (coalesce(row_status1,''P''), row_status2)';
else
execute 'delete from '||c.table_name||' where row_create_datetime>current_date-day1+1';
raise notice 'Table '||c.table_name||' does not have row_status column';
end if;
end loop;
return;
commit;
end;
$$
Your immediate problem is this line:
raise notice 'Table '||c.table_name||' does not have row_status column';
That should be:
raise notice 'Table % does not have row_status column', c.table_name;
However, your function could be improved a bit. In general it is highly recommended to use format() to generate dynamic SQL to properly deal with identifiers. You also can't commit in a function. If you really need that, use a procedure.
create function delete_old_records1(day1 int, row_status1 character default null, row_status2 character default null)
returns void
language plpgsql
as $$
declare
c all_tables1%rowtype;
begin
for c in select * from all_tables1
loop
if exists (SELECT column_name FROM information_schema.columns
WHERE table_schema = 'yard_kondor'
AND table_name = c.table_name
AND column_name = 'row_status') then
execute format('delete from %I
where row_create_datetime > current_date - %J + 1
and row_status in (coalesce(row_status1,%L), row_status2)', c.table_name, day1, 'P');
else
execute format('delete from %I where row_create_datetime > current_date - day1 + 1', c.table_name);
raise notice 'Table % does not have row_status column', c.table_name;
end if;
end loop;
return;
-- you can't commit in a function
end;
$$
Thank you for answer. Now I'm able to save function, but currently I have a problem with running the function.
I started the function with:
DO $$ BEGIN
PERFORM "delete_old_records1"(31,'P','N');
END $$;
I started script with ALT+X (also tried select delete_old_records1(31,'P','N');) and have this error:
SQL Error [42703]: ERROR: column "day1" does not exist
Where: PL/pgSQL function delete_old_records1(integer,character,character) line 11 at EXECUTE statement
SQL statement "SELECT "delete_old_records1"(31,'P','N')"
PL/pgSQL function inline_code_block line 2 at PERFORM

EXECUTE in postgresql

create table x(y number, z character varying(100))
insert into x values (1,'foo');
insert into x values (2,'bar');
do $$
declare
k character varying(100);
begin
EXECUTE 'SELECT z FROM '
||quote_ident(x)
into k;
RAISE NOTICE '%',k;
end;
$$ language 'plpgsql';
ERROR: column "x" does not exist
LINE 2: ||quote_ident(x)
^
QUERY: SELECT 'SELECT z FROM '
||quote_ident(x)
CONTEXT: PL/pgSQL function inline_code_block line 7 at
EXECUTE
SQL state: 42703
May I know what am I doing wrong here and I'm on 9.5. Even EXECUTE format is also giving me the same error.
do $$
declare
k character varying(100);
begin
EXECUTE 'SELECT z FROM '
||quote_ident('x')
into k;
RAISE NOTICE '%',k;
end;
$$ language 'plpgsql';
quotes around 'x' worked for me. Thanks

plpgsql cursor on unnest function

I have a plpgsql function like:
DO
$$
DECLARE
c_id c.id%TYPE;
j_text c.j_options%TYPE;
j_option varchar;
c1 c%ROWTYPE;
begin
CREATE TYPE my_row_type AS (c2 TEXT);
for
--c1 in select c.j_options, c.id from c c
c1 in select * from c
loop
c_id = c1.id;
for
c2 in select * from unnest(string_to_array(c1.j_options,', '))
loop
raise notice 'Value: %, %', c_id, c2.j_options;
end loop;
end loop;
END
$$ language plpgsql;
My issue is this line:
c2 in select * from unnest(string_to_array(c1.j_options,', '))
The sample query I run for eg:
select unnest(string_to_array('1.0, 1.2',', '));
returns 2 rows:
1. 1.0
2. 1.2
I need to loop over these two rows but am not sure what the type of the return of this unnest statement should be or how it should be declared in the declare section.
The error I get when running the script:
ERROR: loop variable of loop over rows must be a record or row variable or
list of scalar variables
LINE 18: c2 in select * from unnest(string_to_array(c1.j_...
From the answer below I get the following error
ERROR: function string_to_array(bytea, unknown) does not exist
LINE 1: select from unnest(string_to_array(c1.j_options,', '))
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
QUERY: select from unnest(string_to_array(c1.j_options,', '))
I don't understand why this would not work in the script. It recognizes that c1.j_options is bytea.
My amended script bit was:
for c2 in
select from unnest(string_to_array(c1.j_options,', '))
loop
raise notice '%', c2;
end loop;
According to string_to_array function signature it returns array of text (text[]) so c2 should to be text or varchar:
do language plpgsql $$
declare
c varchar;
begin
for c in select unnest(string_to_array('1.0, 1.2', ', ')) loop
raise notice '%', c;
end loop;
end; $$;
There is also special loop kind for iterating trough the array elements directly:
do language plpgsql $$
declare
c varchar;
begin
foreach c in array string_to_array('1.0, 1.2', ', ') loop
raise notice '%', c;
end loop;
end; $$;
The error message is "ERROR: function string_to_array(bytea, unknown) does not exist". The bytea type is not text, it is binary value, and for this type the function string_to_array is not defined. There are not any default cast from bytea to text. Hard to say what is solution, because bytea can holds anything. If there are some text, then the type "text" should be used, not bytea.

How to keep looping even error happend?

I wrote a PL/pgsql to batch create index on tables
CREATE OR REPLACE FUNCTION create_index() RETURNS void AS
$BODY$
DECLARE
r INTEGER;
BEGIN
FOR r IN 1..1000
LOOP
EXECUTE format(
' CREATE INDEX idx_abc_id_' || r::text ||
' ON abc_id_' || r::text ||
' USING btree
(key);');
END LOOP;
RETURN;
END
$BODY$
LANGUAGE plpgsql;
it has one problem, if partition abc_500 doesn't exist, then the how create index function will fail and do nothing.
How to make loop keep going through even if create_index made an error on one of the table in between?
I think a better approach would be to not hardcode the number for the loop, but iterate over the existing tables:
CREATE OR REPLACE FUNCTION create_index() RETURNS void AS
$BODY$
DECLARE
r record;
BEGIN
FOR r IN select tablename, regexp_replace(tablename, '[^0-9]+','') as idx_nr
from pg_tables
where tablename ~ 'abc_id_[0-9]+'
LOOP
EXECUTE format('CREATE INDEX %I ON %I USING btree (key)',
'idx_abc_id_'||r.idx_nr,
r.tablename);
END LOOP;
RETURN;
END
$BODY$
LANGUAGE plpgsql;
When you use the format() function is better to use the proper place holders for identifiers.
If you also want to ignore any error when creating the index on an existing table, you need to catch the exception and ignore it:
CREATE OR REPLACE FUNCTION create_index() RETURNS void AS
$BODY$
DECLARE
r record;
msg text;
BEGIN
FOR r IN select tablename, regexp_replace(tablename, '[^0-9]+','') as idx_nr
from pg_tables
where tablename ~ 'abc_id_[0-9]+'
LOOP
BEGIN
EXECUTE format('CREATE INDEX %I ON %I USING btree (key)',
'idx_abc_id_'||r.idx_nr,
r.tablename);
EXCEPTION
WHEN OTHERS THEN
GET STACKED DIAGNOSTICS msg = MESSAGE_TEXT;
RAISE NOTICE 'Could not create index for: %, %', r.idx_nr, msg;
END;
END LOOP;
RETURN;
END
$BODY$
LANGUAGE plpgsql;