Run same script on under different schema - postgres - database

I have a script called populate.sql which contains create tables.
CREATE TABLE "EXAMPLE" (
.................
..............
);
CREATE TABLE "BlaBla" (
..........
........
);
CREATE TABLE ...
This script creates more than 20 tables. I want to run this populate.sql on top of different schemas. Let's say I want to run this script on schema1, schema2 and schema3.
Then I can write;
CREATE SCHEMA IF NOT EXISTS "schema1";
SET SCHEMA 'schema1';
on populate.sql and create those tables on one schema.
how can I create those tables on all schema within one psql command?
As far as I feel I have to do FOR LOOP on psql and create schema first and create tables on top of that scheme.

Tables will get created in the currently set search_path (if not otherwise specifically set in the create statement).
You could use a loop. In that loop you have to set the searchpath to your schema.
DO
$$
DECLARE schemaname text;
BEGIN
FOR i IN 1..3 LOOP
schemaname := 'schema' || i::text;
execute 'CREATE SCHEMA ' || schemaname;
execute 'SET SCHEMA ' || schemaname;
execute 'SET search_path TO ' || schemaname;
-- conent of populate.sql
END LOOP;
END
$$;
You cannot call external scripts inside this do block as mentioned by a_horse_with_no_name in the comments. Therefore this answer is only relevant if you want to extend your populate.sql file and wrap this do block around it.

Related

Create table across all schemas at once

Is it possible to create a table across every schema in your database?
Specifically in Oracle.
I do not want to run the exact same query for all existing schemas.
Solution i have been using is,
Use below query to get all schema names,
select distinct owner FROM all_tables;
Get the result and use regular expression to append/prepend your table creation query.
^ - create table
$ - .tablename \( column1 varchar2(10)\);
run all the resulting queries in oracle work sheet.
You can use a bit of PL/SQL and execute immediate to do this.
For example you can create a table in all schemas if you connect as SYS User and execute the following Script:
begin
for cUsers in (select * from dba_users where account_status ='OPEN')
loop
execute immediate 'create table '||cUsers.username||'.myTable ( id number )';
end loop;
end;

I want to create 50 databases for 50 states dynamically and insert records to related database on tsql?

I want to create 50 databases for 50 states dynamically and insert records of table factinternetsales from adventureworksdw database to related state database, how can I write sql script or built ssis pacakge
Have a look at "dynamic sql". Start with the database like so:
declare #stateName varchar(50) = 'MyState';
declare #cmd varchar(max) = 'create database ' + #stateName + ';';
execute (#cmd);
If you have your states in a table, you can loop over them by using a cursor and this kind of dynamic sql. After creating the databases you can create tables and do INSERTs in the same way.

How to create trigger for all table in postgresql?

I have a trigger, but I need to associate with all tables of the my postgres.
Is there a command like this below?
CREATE TRIGGER delete_data_alldb
BEFORE DELETE
ON ALL DATABASE
FOR EACH ROW
EXECUTE PROCEDURE delete_data();
Well there is no database-wide trigger creation but for all such bulk-admin-operations you could use PostgreSQL system tables to generate queries for you instead of writing them by hand.
In this case you could run:
SELECT
'CREATE TRIGGER '
|| tab_name
|| ' BEFORE DELETE ON ALL DATABASE FOR EACH ROW EXECUTE PROCEDURE delete_data();' AS trigger_creation_query
FROM (
SELECT
quote_ident(table_schema) || '.' || quote_ident(table_name) as tab_name
FROM
information_schema.tables
WHERE
table_schema NOT IN ('pg_catalog', 'information_schema')
AND table_schema NOT LIKE 'pg_toast%'
) tablist;
This will get you set of strings which are SQL commands like:
CREATE TRIGGER schema1.table1 BEFORE DELETE ON ALL DATABASE FOR EACH ROW EXECUTE PROCEDURE delete_data();
CREATE TRIGGER schema1.table2 BEFORE DELETE ON ALL DATABASE FOR EACH ROW EXECUTE PROCEDURE delete_data();
CREATE TRIGGER schema1.table3 BEFORE DELETE ON ALL DATABASE FOR EACH ROW EXECUTE PROCEDURE delete_data();
CREATE TRIGGER schema2.table1 BEFORE DELETE ON ALL DATABASE FOR EACH ROW EXECUTE PROCEDURE delete_data();
CREATE TRIGGER schema2."TABLE2" BEFORE DELETE ON ALL DATABASE FOR EACH ROW EXECUTE PROCEDURE delete_data();
...
etc
You just need to run them at once (either by psql or pgAdmin).
Now some explanation:
I select names of tables in my database using information_schema.tables system table. Because there are data of literally all tables, remember to exclude pg_catalog and information_schema schemas and toast tables from your select.
I use quote_ident(text) function which will put string inside double quote signs ("") if necessary (ie. names with spaces or capital letters require that).
When I have list of tables names I just concatenate them with some static strings to get my SQL commands.
I write that command using sub-query because I want you to get better idea of what's going on here. You may write a single query by putting quote_ident(table_schema) || '.' || quote_ident(table_name) in place of tab_name.
A conveniently encapsulated version of Gabriel's answer. This time I am using the trigger to update a column named update_dt datetime granted to be part of any table in the public schema of the current database.
--
-- function: tg_any_update_datetime_fn
-- when: before insert or update
--
create or replace function tg_any_update_datetime_fn ()
returns trigger
language plpgsql as $$
begin
new.update_dt = now();
return new;
end;
$$;
--
-- function: ddl_create_before_update_trigger_on_all_tables
-- returns: Create a before update trigger on all tables.
--
create or replace procedure ddl_create_before_update_trigger_on_all_tables ()
language plpgsql as $$
declare
_sql varchar;
begin
for _sql in select concat (
'create trigger tg_',
quote_ident(table_name),
'_before_update before update on ',
quote_ident(table_name),
' for each row execute procedure tg_any_update_datetime_fn ();'
)
from
information_schema.tables
where
table_schema not in ('pg_catalog', 'information_schema') and
table_schema not like 'pg_toast%'
loop
execute _sql;
end loop;
end;
$$;
-- create before update trigger on all tables
call ddl_create_before_update_trigger_on_all_tables();
On my DDL scripts I use a large number of such ddl_ functions that have only meaning at DDL time. To remove them from the database use
--
-- function: ddl_drop_ddl_functions
-- returns: Drop all DDL functions.
-- since: 1.1.20
--
create or replace procedure ddl_drop_ddl_functions ()
language plpgsql as $$
declare
r record;
_sql varchar;
begin
for r in
select oid, prokind, proname
from pg_proc
where pronamespace = 'public'::regnamespace
and proname ilike 'ddl_%'
loop
case r.prokind
when 'a' then _sql = 'aggregate';
when 'p' then _sql = 'procedure';
else _sql = 'function';
end case;
_sql = format('drop %s %s', _sql, r.oid::regprocedure);
execute _sql;
end loop;
end
$$;

How to Exclude Data for Specific Tables

I am using mysqldump to create a canonical installation script for a MySQL database. I would like to dump the data for able half of the tables in the database, but exclude the data from the other tables. I am aware of the following two commands:
--no-data
--ignore-table
But the first applies to all tables, and I believe the second excludes the table entirely from the dump (e.g. create statements) not just the data in the table. Anyone know how to use mysqldump to achieve my goal?
EDIT:
found a near duplicate question: mysqldump entire structure but only data from selected tables in a single command
How about running two separate calls to mysqldump? One to create the database and ignore the tables you don't want data from. The other to just create the remaining tables without data. You could either run the two scripts separately, or concatenate them together to create a final script.
There is one other option to get everything done (in a single call to mysql itself) but it should probably never be attempted.
In tribute to H.P. Lovecraft, (and based upon Anuya's stored procedure to create INSERT statements) here's The Stored Procedure Which Must Not Be Called:
Note: This unholy, arcane stored procedure would only be run by a madman and is presented below purely for educational purposes.
DELIMITER $$
DROP PROCEDURE IF EXISTS `pseudoDump` $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `pseudoDump`(
in_db varchar(20),
in_tables varchar(200),
in_data_tables varchar(200)
)
BEGIN
DECLARE Whrs varchar(500);
DECLARE Sels varchar(500);
DECLARE Inserts varchar(200);
DECLARE tablename varchar(20);
DECLARE ColName varchar(20);
SELECT `information_schema`.`TABLE_NAME` INTO tablename FROM TABLES WHERE TABLE_SCHEMA = in_db AND TABLE_NAME IN ( in_tables );
tabdumploop: LOOP
SHOW CREATE TABLE tablename;
LEAVE tabdumploop;
END LOOP tabdumploop;
SELECT `information_schema`.`TABLE_NAME` INTO tablename FROM TABLES WHERE TABLE_SCHEMA = in_db ;
datdumploop: LOOP
SELECT group_concat(concat('concat(\'"\',','ifnull(',column_name,','''')',',\'"\')')) INTO #Sels from `information_schema`.`COLUMNS` where table_schema=in_db and table_name=tablename;
SELECT group_concat('`',column_name,'`') INTO #Whrs from `information_schema`.`COLUMNS` where table_schema=in_db and table_name=tablename;
SET #Inserts=concat("select concat('insert IGNORE into ", in_db,".",tablename," values(',concat_ws(',',",#Sels,"),');') as MyColumn from ", in_db,".",tablename, " where 1 group by ",#Whrs, ";");
PREPARE Inserts FROM #Inserts;
EXECUTE Inserts;
LEAVE datdumploop;
END LOOP datdumploop;
END $$
DELIMITER ;
... thankfully, I was saved from witnessing the soul-wrenching horror this procedure must surely wreak by MySQL Bug #44009 ...
mysqldump -u user -h host.example.com -p database table1 table2 table3
You might find what you need here:
http://www.electrictoolbox.com/mysqldump-selectively-dump-data/
Using where statements is probably the easiest way to achieve what you are trying to do.

mysqldump table names prefix

I have two mysql databases that have almost the same structure and representing the data of the same web app but one of them represents the current version and second one was made long time ago.
How can I create the database with both dumps inside but with old_ prefix for tables from the first and new_ prefix for tables from the second database?
Is there any mysqldump options to setup the prefix or other solution?
A "mysqldump file" is just a text file full of SQL statements, so you can make quick modifications like these in a text editor.
1) Dump the two databases individually.
2) Edit the "old" dump file:
add the correct use mydatabase; line
do a search and replace to add old_ in front of the table names.
3) Then, cat dump1 dump2 > combined_dump
4) mysql < combined_dump
This sed script is perhaps a little safer. Save it to a file and use sed -f to filter the dump file.
s/\(-- Table structure for table `\)\([^`]\+\)\(`\)/\1xyzzy_\2\3/
s/\(DROP TABLE IF EXISTS `\)\([^`]\+\)\(`\)/\1xyzzy_\2\3/
s/\(CREATE TABLE `\)\([^`]\+\)\(` (\)/\1xyzzy_\2\3/
s/\(-- Dumping data for table `\)\([^`]\+\)\(`\)/\1xyzzy_\2\3/
s/\(\/\*!40000 ALTER TABLE `\)\([^`]\+\)\(` DISABLE KEYS \*\/\)/\1xyzzy_\2\3/
s/\(LOCK TABLES `\)\([^`]\+\)\(` WRITE\)/\1xyzzy_\2\3/
s/\(INSERT INTO `\)\([^`]\+\)\(` VALUES (\)/\1xyzzy_\2\3/
s/\(\/\*!40000 ALTER TABLE `\)\([^`]\+\)\(` ENABLE KEYS \*\/\)/\1xyzzy_\2\3/
Search and replace xyzzy_ with your desired table prefix.
Restore both the databases as it is.
Use the following stored procedure to move all the tables from one DB to another DB after adding the prefix.
After moving delete the source database.
This stored procedure gets the table list from MySQL's inmemory tables in information_schema and automatically moves to another DB using the RENAME command.
DELIMITER $$
USE `db`$$
DROP PROCEDURE IF EXISTS `renameDbTables`$$
CREATE DEFINER=`db`#`%` PROCEDURE `renameDbTables`(
IN from_db VARCHAR(20),
IN to_db VARCHAR(30),
IN to_name_prefix VARCHAR(20)
)
BEGIN
/*
call db.renameDbTables('db1','db2','db_');
db1.xxx will be renamed to db2.db_xxx
*/
DECLARE from_state_table VARCHAR(20) DEFAULT '';
DECLARE done INT DEFAULT 0;
DECLARE b VARCHAR(255) DEFAULT '';
DECLARE cur1 CURSOR FOR SELECT TABLE_NAME FROM information_schema.TABLES
WHERE TABLE_SCHEMA=from_db;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
OPEN cur1;
REPEAT
FETCH cur1 INTO from_state_table;
IF NOT done THEN
-- select from_state_table;
SET #QUERY = '';
SET #QUERY = CONCAT(#QUERY,'RENAME TABLE ',from_db,'.', from_state_table,' TO ',to_db,'.', to_name_prefix, from_state_table,';');
-- SELECT #query;
PREPARE s FROM #QUERY;
EXECUTE s;
DEALLOCATE PREPARE s;
END IF;
UNTIL done END REPEAT;
CLOSE cur1;
END$$
DELIMITER ;
Import them into different databases. Say they're called newdb and olddb. Then you can copy table1 to old_table1 like:
insert into newdb.old_table1
select *
from olddb.table1
If you have a huge number of tables, generate a script to copy them:
select concat('insert into newdb.old_', table_name,
'select * from olddb.', table_name, ';')
from information_schema.tables
where table_schema = 'olddb'
I have done the following using mysqldump and sed in the past, but I'll admit it may only be effective for one table at a time.
$ mysqldump -u user --password=mypass MyDB MyTable | sed s/MyTable/old_Mytable/ | mysql -u other_user -p NewDB
You could create a shell script with a list of the commands, one for each table, or perhaps another user has a way to modify this to work against multiple tables effectively in one shot.
Peer
I may be misunderstanding the problem, but it sounds like you want to dump the 2 databases into a single SQL file to be used to restore the dbs, with the old tables going into one schema and the new tables going into another.
IF that's what you are trying to do, the simplest approach is just to insert the proper "use database" command before each dump.
Like so:
echo "use old_db;" > /tmp/combined_dump.sql
mysqldump old_db >> /tmp/combined_dump.sql
echo "use new_db;" >> /tmp/combined_dump.sql
mysqldump new_db >> /tmp/combined_dump.sql
Run the following query:
SELECT Concat('ALTER TABLE ', TABLE_NAME, ' RENAME TO my_prefix_', TABLE_NAME, ';') FROM information_schema.tables WHERE table_schema = 'my_database'
The output of which is several queries. Then run those queries.
This won't work if there's constraints, or other complicated things, but for simple DBs this works fine.

Resources