I'm new to plpgsql. I'm trying to run a simple query in plpgsql using a variable as table name in plpgsql. But the variable is being interpreted as the table name instead of the value of the variable being interpreted as variable name.
DECLARE
v_table text;
z_table text;
max_id bigint;
BEGIN
FOR v_table IN
SELECT table_name
FROM information_schema.tables
WHERE table_catalog = 'my_database'
AND table_schema = 'public'
AND table_name not like 'z_%'
LOOP
z_table := 'z_' || v_table;
SELECT max(id) from z_table INTO max_id;
DELETE FROM v_table where id > max_id;
END LOOP;
Some background information. For every table in my database, I have another table starting with "z_". E.g. for a table called "employee" I have identical table called "z_employee". z_employee
contains the same set of data as employee. I use it to restore the employee table at the start of every test.
When I run this function I get the following error:
ERROR: relation "z_table" does not exist
LINE 1: SELECT max(id) from z_table
My guess is that I'm not allowed to use the variable z_table
in the SQL query. At least not the way I'm using it here. But I don't know how it's supposed to be done.
An assignment of a value to a PL/pgSQL variable is written as: variable { := | = } expression ; As explained previously, the expression in such a statement is evaluated by means of an SQL SELECT command sent to the main database engine.
Every table has a name, every column has a name, and so on. PostgreSQL uses a single data type to define all object names: the name type. A value of type name is a string of 63 or fewer characters1. A name must start with a letter or an underscore; the rest of the string can contain letters, digits, and underscores.
A type cast specifies a conversion from one data type to another. PostgreSQL accepts two equivalent syntaxes for type casts, the PostgreSQL-specific value::type and the SQL-standard CAST(value AS type) .
The general syntax of a variable declaration is: name [ CONSTANT ] type [ COLLATE collation_name ] [ NOT NULL ] [ { DEFAULT | := | = } expression ]; The DEFAULT clause, if given, specifies the initial value assigned to the variable when the block is entered.
Use dynamic SQL with EXECUTE
, simplify, and escape identifiers properly:
CREATE OR REPLACE FUNCTION f_test()
RETURNS void AS
$func$
DECLARE
v_table text;
BEGIN
FOR v_table IN
SELECT table_name
FROM information_schema.tables
WHERE table_catalog = 'my_database'
AND table_schema = 'public'
AND table_name NOT LIKE 'z_%'
LOOP
EXECUTE format('DELETE FROM %I v WHERE v.id > (SELECT max(id) FROM %I)'
, v_table, 'z_' || v_table);
END LOOP;
END
$func$ LANGUAGE plpgsql;
Table names may need to be quoted to defend against syntax errors or even SQL injection! I use the convenient format()
to concatenate the DELETE
statement and escape identifiers properly.
SELECT
would be more expensive. You can do it all with a single DELETE
statement.Related:
Aside:
You might use the (slightly faster) system catalog pg_tables
instead:
SELECT tablename
FROM pg_catalog.pg_tables
WHERE schemaname = 'public'
AND tablename NOT LIKE 'z_%'
See:
table_catalog
in information_schema.tables
has no equivalent here. Only tables of the current database are visible anyway. So the above predicate WHERE table_catalog = 'my_database'
produces an empty result set when connected to the wrong database.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With