How to change schema of multiple PostgreSQL tables in one operation? - postgresql

I have a PostgreSQL 9.1 database with 100 or so tables that were loaded into the 'public' schema. I would like to move those tables (but not all of the functions in 'public') to a 'data' schema.
I know that I can use the following to move 1 table at a time.
ALTER TABLE [tablename] SET SCHEMA [new_schema]
Is it possible to move all of the tables to the new schema in one operation? If so, what would be the most efficient way to accomplish this task?

DO will do the trick:
DO
$$
DECLARE
row record;
BEGIN
FOR row IN SELECT tablename FROM pg_tables WHERE schemaname = 'public' -- and other conditions, if needed
LOOP
EXECUTE format('ALTER TABLE public.%I SET SCHEMA [new_schema];', row.tablename);
END LOOP;
END;
$$;

-- ####### USING DBEAVER WHICH SUPPORT VARIABLES ########
-- ### ANSWER_1 -- USING DO ###--------
-- Step1: Set variables one by one
#set _SCHEMA = 'public'
#set _COLUMN = 'dml_status'
#set _DATA_TYPE = 'integer'
#set _DEFAULT = '1'
-- Step2: Call the below procedure
DO
$$
DECLARE
row record;
query varchar;
BEGIN
FOR ROW IN SELECT table_name FROM information_schema.tables WHERE table_schema = ${_SCHEMA}
LOOP
query :='ALTER TABLE public.' || quote_ident(row.table_name) ||' ADD COLUMN IF NOT EXISTS '||${_COLUMN} || ' ' || ${_DATA_TYPE} ||' not null default ' || ${_DEFAULT} || ';' ;
execute query;
END LOOP;
END;
$$;
-- ### ANSWER_2 -- STORE PROCEDURE FN ###--------
DROP FUNCTION addColumnToMultipleTables cascade;
create or replace function addColumnToMultipleTables()
returns void
LANGUAGE 'plpgsql'
as $$
DECLARE
row record;
query varchar;
BEGIN
FOR ROW IN SELECT table_name FROM information_schema.tables WHERE table_schema = ${_SCHEMA}
LOOP
query :='ALTER TABLE public.' || quote_ident(row.table_name) ||' ADD COLUMN IF NOT EXISTS '||${_COLUMN} || ' ' || ${_DATA_TYPE} ||' not null default ' || ${_DEFAULT} || ';' ;
raise info 'query : % ', query;
execute query;
END LOOP;
END;
$$;
select addColumnToMultipleTables();

Related

How to execute query statements generated in pgAdmin4?

I have the following query which generates a list of ALTER TABLE statements in the 'Data Output' field in pgAdmin4. I can copy & paste them back into the query pane and execute them there.
select 'ALTER TABLE ' || table_name || ' OWNER TO myuser;' from information_schema.tables where table_schema = 'public';
How can I execute the generated statements without having to copy & paste them?
You can use the function for that.
CREATE OR REPLACE FUNCTION change_permission_table()
RETURNS VOID AS $$
DECLARE
rec text;
BEGIN
FOR rec IN SELECT 'ALTER TABLE ' || table_name || ' OWNER TO maz;' FROM information_schema.tables WHERE table_schema = 'public'
LOOP
EXECUTE rec;
END LOOP;
END;
$$ LANGUAGE plpgsql;
-- Run the function to change the permission
SELECT change_permission_table()

Inserting into dynamic table partition in postgresql with return

I created a table partition that will create a table if it is not yet existing the table names are on a monthly basis. I need this function to return the inserted ID but I'm getting this error of column "partition" does not exist it seems that my schema(partition) is considered column in this code
CREATE OR REPLACE FUNCTION partition.itinerary_partition_function()
RETURNS TRIGGER AS
$BODY$
DECLARE
reflowId bigint;
_tablename text;
_startyear text;
_startmonth text;
_fulltablename text;
BEGIN
--Takes the current inbound "time" value and determines when midnight is for the given date
_startyear := to_char(now(), 'YYYY');
_startmonth := to_char(now(), 'MM');
_tablename := 'itinerary_'||_startyear || '_' || _startmonth;
_fulltablename := 'partition.' || _tablename;
-- Check if the partition needed for the current record exists
PERFORM 1
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r'
AND c.relname = _tablename
AND n.nspname = 'partition';
-- If the partition needed does not yet exist, then we create it:
-- Note that || is string concatenation (joining two strings to make one)
IF NOT FOUND THEN
EXECUTE 'CREATE TABLE partition.' || quote_ident(_tablename) || '()INHERITS (partition.itinerary)';
-- Table permissions are not inherited from the parent.
-- If permissions change on the master be sure to change them on the child also.
EXECUTE 'ALTER TABLE partition.' || quote_ident(_tablename) || ' OWNER TO postgres';
-- Indexes are defined per child, so we assign a default index that uses the partition columns
EXECUTE 'CREATE INDEX ' || quote_ident(_tablename||'_indx1') || ' ON partition.' || quote_ident(_tablename) || ' (id)';
END IF;
BEGIN
EXECUTE format('INSERT INTO %I SELECT $1.*', "partition." || _tablename)
USING NEW;
RETURN NEW;
END;
END;
$BODY$
LANGUAGE plpgsql;
After this code I am calling it in another insert function
CREATE OR REPLACE FUNCTION partition.insert_data(username text,jsonData jsonb) RETURNS bigint AS
$$
DECLARE reflowId bigint;
BEGIN
INSERT INTO reflow_partition.itinerary(username, data)
VALUES (username, jsonData) RETURNING id;
END;
$$
LANGUAGE plpgsql;
Try changing this:
EXECUTE format('INSERT INTO %I SELECT $1.*', "partition." || _tablename)
to this:
EXECUTE format('INSERT INTO %1$I.%2$I SELECT $1.*', 'partition', _tablename)

Simple PostgreSQL plpgsql to create a new table using existing table

I'm new to plpgsql. I'm sure there is some really simple way to do this, but for some reason I'm having a lot of trouble trying to figure out how to do this.
I'm simply trying to loop through the list of existing tables and execute
CREATE TABLE z_existing_table_name AS SELECT * FROM existing_table_name WITH DATA
So far, I have this:
CREATE OR REPLACE FUNCTION create_backup_row()
RETURNS RECORD
AS $$
DECLARE
row RECORD;
BEGIN
FOR row IN SELECT * FROM information_schema.tables WHERE table_catalog = 'my_db' and table_schema = 'public'
LOOP
EXECUTE 'CREATE TABLE z_' || t.table_name || ' as ' || t.table_name
END LOOP;
END;
$$ LANGUAGE plpgsql;
It would be an added bonus if I can make this function re-runnable. Something like drop table if exist then create table ...
#Steven, use below procedure,
-- Function: create_backup_row()
-- DROP FUNCTION create_backup_row();
CREATE OR REPLACE FUNCTION create_backup_row()
RETURNS integer AS
$BODY$
DECLARE
v_table text;
BEGIN
FOR v_table IN
SELECT table_name
FROM information_schema.tables
WHERE table_catalog = 'my_db'
AND table_schema = 'public'
AND table_name not ilike '%z_%' -- to skip the table with z_ when we rerun it.
LOOP
EXECUTE ' DROP TABLE IF EXISTS z_' || v_table ;
EXECUTE 'CREATE TABLE z_' || v_table || ' as SELECT * FROM ' || v_table ;
END LOOP;
return 1;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION create_backup_row()
OWNER TO postgres;

In postgres how can I delete all columns that share the same prefix

I have been using the following code for dropping all tables that share the same prefix (in this case delete all tables that their name starts with 'supenh_'):
DO
$do$
DECLARE
_tbl text;
BEGIN
FOR _tbl IN
SELECT quote_ident(table_schema) || '.'
|| quote_ident(table_name) -- escape identifier and schema-qualify!
FROM information_schema.tables
WHERE table_name LIKE 'supenh_' || '%' -- your table name prefix
AND table_schema NOT LIKE 'pg_%' -- exclude system schemas
LOOP
-- RAISE NOTICE '%',
EXECUTE
'DROP TABLE ' || _tbl;
END LOOP;
END
$do$;
Is there a way to amend this code / or to use a different script in order to delete from one specific table all the columns that start with the same prefix (for example, 'patient1_') ?
You could write it as PL/pgSQL function:
CREATE OR REPLACE FUNCTION drop_columns_with_prefix(tbl_name TEXT, column_prefix TEXT) RETURNS VOID AS
$BODY$
DECLARE
_column TEXT;
BEGIN
FOR _column IN
SELECT quote_ident(column_name)
FROM information_schema.columns
WHERE table_name = tbl_name
AND column_name LIKE column_prefix || '%'
AND table_schema NOT LIKE 'pg_%'
LOOP
-- RAISE NOTICE '%',
EXECUTE
'ALTER TABLE ' || tbl_name || ' DROP COLUMN ' || _column;
END LOOP;
END
$BODY$
LANGUAGE plpgsql VOLATILE;
Call it using:
SELECT drop_columns_with_prefix('tbl_name', 'prefix_');
Or if you don't want to use it as a function:
DO
$do$
DECLARE
_column TEXT;
BEGIN
FOR _column IN
SELECT quote_ident(column_name)
FROM information_schema.columns
WHERE table_name = 'tbl_name'
AND column_name LIKE 'prefix_%'
AND table_schema NOT LIKE 'pg_%'
LOOP
-- RAISE NOTICE '%',
EXECUTE
'ALTER TABLE tbl_name DROP COLUMN ' || _column;
END LOOP;
END
$do$

Updating multiple records in multiple tables using trigger

I am trying to write a trigger function in postgres that will update multiple records in multiple other tables when a record to a table is added.
For example I have the schema 'survey' and a table called 'a_household'.
Within this schema there are multiple tables that have a field 'hh_id' that reference the id of the household table.
The hh_id of all these tables only references the id of the household table if other attributes of the tables match.
Currently my failed coding looks like:
DECLARE
tables text[] = ARRAY['b_member','f_firewood'];
table_name text;
r record;
BEGIN
FOREACH table_name IN ARRAY tables
LOOP
INSERT INTO survey.table_name(hh_id) values (NEW.id)
SELECT * FROM survey.table_name
WHERE survey.table_name.a= NEW.b;
END LOOP;
END;
I am failing completely - any advice appreciated.
My second attempt (after reading the response below) was:
DECLARE
tables text[] = ARRAY['b_member'];
table_name text;
BEGIN
if tg_op='INSERT' then
FOREACH table_name IN ARRAY tables
LOOP
EXECUTE 'UPDATE ' || 'survey.' || table_name || ' SET hh_id = '||NEW.id||' FROM household.a_household WHERE (select survey.b_member.odk_parentkey from survey.b_member
where survey.b_member.odk_key = "'||NEW.odk_key||'");';
END LOOP;
end if;
return new;
END;
but I am returned the error message 'column 'hgd' does not exist. This is the value of NEW.odk_key
Becky
CREATE OR REPLACE FUNCTION survey.insert_in_other_tables()
RETURNS trigger AS
$BODY$
declare tables text[] = ARRAY['b_member','f_firewood'];
table_name text;
BEGIN
if tg_op='INSERT' then
FOREACH table_name IN ARRAY tables
LOOP
EXECUTE 'INSERT INTO ' || 'survey.' || table_name || '(hh_id) values ('||NEW.id||');';
END LOOP;
end if;
return new;
end;$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;