Dynamic query splicing error,a fuction use falie - postgresql

I want to use the cursor to get the count(data) in all tables under the schema I need.
But I am unfamiliar with SQL, and I still cannot pass the following code:
CREATE OR REPLACE FUNCTION check_tool.get_nae(v_dbName character varying)
RETURNS numeric
LANGUAGE edbspl
SECURITY DEFINER
AS $function$
numInsert numeric;
numCal numeric;
v_result numeric;
query TEXT DEFAULT '';
cursor c_pj is
select t.table_schema::text as tableSchema,
t.table_name::text as tableName
from information_schema.tables t
where t.table_catalog = v_dbName
and t.table_type = 'BASE TABLE'
and t.table_schema in (select schema_name from check_tool.img_schema where dbName = v_dbName);
BEGIN
v_result := -1;
numInsert := 0;
for r_pj in c_pj loop
query := 'select count(*) from '||tableSchema||'.'||tableName||';'; -- select count(*) from "item"."project";
execute query into numCal;
insert into check_tool.img_result(schema_name,table_name,num) values (r_pj.tableSchema,r_pj.tableName,numCal);
numInsert := numInsert + 1;
if numInsert > 1000 then
numInsert := 0;
commit;
end if;
end loop;
commit;
v_result := 0;
RETURN v_result;
EXCEPTION
WHEN others THEN
RETURN v_result;
END get_nae$function$
;
/
I also tried concat() and quote_ident(), but the result is not ideal.

Dynamic SQL should be constructed using format() to better handle identifiers. In Postgres you can't commit inside a function, only in a procedure. Committing in a loop rarely improves the performance to begin with, so I would just skip that. I also wouldn't hide the real error (by just returning -1 or 0) but simply let any exception reach the caller of the procedure. And language edbspl is nothing I know, but in PL/pgSQL I would write it like this:
CREATE OR REPLACE PROCEDURE check_tool.get_nae(v_dbName character varying)
LANGUAGE plpgsql
AS
$body$
declare --<< required in PL/pgSQL to declare variables
numcal numeric;
query TEXT DEFAULT '';
l_rec record;
BEGIN
for l_rec in select t.table_schema::text as tableschema, t.table_name::text as tablename
from information_schema.tables t
where t.table_catalog = v_dbName
and t.table_type = 'BASE TABLE'
and t.table_schema in (select schema_name from check_tool.img_schema where dbName = v_dbName)
loop
query := format('select count(*) from %I.%I', l_rec.tableschema, l_rec.tablename);
execute query into numcal;
insert into check_tool.img_result(schema_name,table_name,num) values (l_rec.tableschema, l_rec.tablename, numcal);
end loop;
commit;
END;
$body$
;
Note that the condition t.table_catalog = v_dbName is actually useless, because you can't query tables that are not in the current database anyway.
Note that you don't really need a stored procedure to do this. You can use query_to_xml() to do this in a single SQL query by adjusting this answer
insert into check_tool.img_result(schema_name,table_name,num)
select table_schema,
table_name,
(xpath('/row/cnt/text()', xml_count))[1]::text::int as row_count
from (
select table_name, table_schema,
query_to_xml(format('select count(*) as cnt from %I.%I', table_schema, table_name), false, true, '') as xml_count
from information_schema.tables
where t.table_type = 'BASE TABLE'
and t.table_schema in (select schema_name from check_tool.img_schema)
) t;

Related

Postgres while insert from md5(variable) it inserts column name not column values

below is my postgres procedure
create or replace procedure ds_rs.test_ad () as $$
declare
i integer;
v_command text;
rcd record;
v_count text; v_pk_1 text; v_pk_2 text;
v_account_id varchar(100);
v_query text;
begin
for rcd in ( select t.table_name as object_name, a.table_schema, a.table_name from TLDEPLOY.TABLES_TO_VERIFY t
left join information_schema.tables a on concat
(COALESCE(a.table_schema, ''),'.',COALESCE(a.table_name, '')) = t.table_name order by t.table_name) loop
select count(*), max(column_name), min(column_name) into v_count, v_pk_1, v_pk_2
from information_schema.table_constraints c
join information_schema.columns cl on cl.table_name =c.table_name
where c.constraint_type ='PRIMARY KEY'
and c.table_name =rcd.table_name
and c.TABLE_SCHEMA=rcd.table_schema
and cl.ordinal_position =1
and cl.TABLE_SCHEMA=rcd.table_schema ;
v_command :=('insert into ds_rs.test_table (pk_1,pk_2) select '||v_pk_1||' , '||md5(v_pk_2::text)||' from ds_rs.account');
execute v_command;
end loop;
end; $$
language plpgsql;
while insert into table this query -> md5(v_pk_2::text) -> hash column name and inserts - i want column values to hash and insert
when used as normal - select md5(account_id::text) from account it gives correct value - but when passed as variable it inserts column name not column data

Union multiple tables

I tried to follow this answer
Dynamic UNION ALL query in Postgres
but I am getting
ERROR: syntax error at or near "record"
DECLARE
rec record;
strSQL text;
BEGIN
FOR
rec in
select table_name
from
information_schema.tables
where
table_name like 'history%'
loop
strSQL : = strSQL || 'Select * from' || rec.table_schema ||'.'|| rec.table_name || ' UNION ';
end loop;
-- remove last ' UNION ' from strSQL
--strSQL := 'Select row_number() over(order by rowid ) as row_num from (' || strSQL || ')';
execute strSQL;
anyone have any ideas?
background:
history table is moved every night to its own table with the date appended.
so history04242018 for each table name, any better way to get the data for multiple days?
edit: tables will always have the same amount of columns so the union should be fine
edit2: I only have read access.
update
with the suggestion of using an anonymous code block I am now using the following:
DO
$$
declare
strSQL text;
begin
select
string_agg(format('select * from %I.%I', table_schema, table_name), E' union\n')
into strSQL
from information_schema.tables
where table_name like 'history%';
execute strSQL ;
end $$;
however I now get the error
Describe Error: Failed to retrieve EXPLAIN plan(s): ERROR: syntax
error at or near "DO" Position: 58
0 record(s) affected
declare, for, loop, execute are the parts of the plpgsql, not plain sql (declare could be used in the plain sql but in different meaning). So you should to wrap your code into anonymous block or into function if you want to return some data from it:
create function get_history(p_day int)
returns table (<structure of history tables here>)
-- or
-- returns setof <history table name>
language plpgsql
as $$
declare
strSQL text;
begin
select
string_agg(format('select * from %I.%I', table_schema, table_name), E' union\n')
into strSQL
from information_schema.tables
where table_name like to_char(p_day, '"history__"FM09%');
return query execute strSQL;
end $$;
Also look at the Table partitioning (choose your PostgreSQL version at the top of article).
Update
However there are several ways to return query data from anonymous plpgsql block without changing DB schema: cursors and prepared statements.
IMO second one is simpler a bit, so:
do $$
declare
strSQL text;
begin
select
string_agg(format('select * from %I.%I', table_schema, table_name), E' union\n')
into strSQL
from information_schema.tables
where table_name like to_char(p_day, '"history__"FM09%');
-- Prepend "prepare", change the "foo" name as you wish
strSQL := 'prepare foo as ' || strSQL;
execute strSQL;
end $$;
-- Usage
execute foo;
-- And deallocate prepared statement when it does not need anymore:
deallocate foo;
Simple working example

nested CURSOR on information schema

I am doing nested cursor using information schema to SAVE my time to insert on all tables.
I am getting error
[42P01] ERROR: relation "rec2" does not exist
Where: PL/pgSQL function test() line 22 at SQL statement
Please help me to resolve this issue.
Below is the code, any help is greatly appreciated.
Thanking in advance.
CREATE OR REPLACE FUNCTION test()
RETURNS VOID AS
$$
DECLARE
rec1 RECORD;
rec2 RECORD;
BEGIN
FOR rec1 IN
SELECT
C.table_name,
C.column_name
FROM "information_schema"."columns" AS C
WHERE C.table_schema = 'prod1'
AND C.table_name LIKE 'prd_tbl%'
AND C.column_name <> 'id'
ORDER BY C.table_name ASC
LOOP
FOR rec2 IN
SELECT table_name
FROM "information_schema"."tables"
WHERE table_schema = 'ec2'
ORDER BY table_name ASC
LOOP
SELECT rec2.*
FROM rec2
EXCEPT
SELECT rec1.column_name.*
FROM rec1;
END LOOP;
END LOOP;
END;
$$
LANGUAGE plpgsql STABLE;

Loop through all user tables and insert row in each

for some reason I just can not figure this out. I have a seperate schema in PostgreSQL for notification related tables for each user connected to the server. My plan is to have each user create a TEMP table to receive extra notification info from since Xojo doesn't support PostgreSQL payloads.
I feel like I'm starting to get close so I'll just post my code that is in my trigger function.
DECLARE
my_table RECORD;
BEGIN
FOR my_table IN
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'information_schema'
LOOP
INSERT INTO my_table.table_name (effected_row_id)
VALUES (NEW.effected_row_id);
END LOOP;
END;
Tell me if I'm wrong, but I believe my main problem is figuring out how to use the table name returned from the SELECT statement in the INSERT statement.
EDIT:
This is my current trigger function
-- Function: notification.my_insert_trigger_function()
-- DROP FUNCTION notification.my_insert_trigger_function();
CREATE OR REPLACE FUNCTION notification.my_insert_trigger_function()
RETURNS trigger AS
$BODY$DECLARE
my_table RECORD;
BEGIN
FOR my_table IN
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'notification' AND table_name <> 'notification_global' AND table_name <> 'switcher'
LOOP
EXECUTE(FORMAT($f$
INSERT INTO %s (effected_row_username)
VALUES (%s);
$f$, 'notification.' || my_table.table_name, NEW.effected_row_username));
END LOOP;
RETURN new;
END;$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION notification.my_insert_trigger_function()
OWNER TO serveradmin;
You need to use dynamic commands in your trigger function.
The funcion format() is often very helpful.
DECLARE
my_table RECORD;
BEGIN
FOR my_table IN
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'information_schema'
LOOP
EXECUTE(FORMAT($f$
INSERT INTO %s (effected_row_id)
VALUES (%s);
$f$, my_table.tablename, NEW.effected_row_id));
END LOOP;
END;

Syntax error while creating function in postgresql

I got a syntax error while creating a procedure in postgresql.Here I attached my code.I got a error syntax error near "Continue"
create function patient_form_values() RETURNS void AS
$$ begin
DECLARE columnName varchar(200) ;
DECLARE done boolean default true;
DECLARE CONTINUE handler for not found set done = false;
DECLARE cur1 cursor for select distinct COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = 'currentdiagnosis';
open cur1;
read_loop : loop
fetch from cur1 into columnName;
if done then leave read_loop;
end if;
set #insertValues := concat('INSERT INTO patient_form_temp(patient_id, form_template_id, creator_id, created_date)
SELECT c.patient_id as patient_id, 41 AS form_template_id, 2 AS creator_id, c.created_date AS created_date
FROM currentdiagnosis c
WHERE c.', columnName,' IS NOT NULL GROUP BY c.patient_id, c.created_date');
select #insertValues;
prepare stmt from #insertValues;
execute stmt;
end loop;
close cur1;
end ;
$$ LANGUAGE plpgsql
You are trying to use a MySQL (or other DB?) function in PostgreSQL. There is no concept of CONTINUE HANDLER in PostgreSQL, so you have to convert the function into PostgreSQL format.
drop FUNCTION if exists migratePartnerAdvertiser();
CREATE OR REPLACE FUNCTION migratePartnerAdvertiser() RETURNS int4 AS '
DECLARE r RECORD;
BEGIN
FOR r IN select distinct COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = ''currentdiagnosis'' and table_schema=''public'' LOOP
EXECUTE concat(''INSERT INTO patient_form_temp(patient_id, form_template_id, creator_id, created_date) SELECT c.patient_id as patient_id, 41 AS form_template_id, 2 AS creator_id, c.reg_date AS created_date FROM currentdiagnosis c WHERE c.'' , r.column_name , '' IS NOT NULL GROUP BY c.patient_id, c.reg_date'');
END LOOP;
return 1;
END;
' LANGUAGE plpgsql;