How to delete every table in a specific schema in postgres? - sql

How do I delete all the tables I have in a specific schema? Only the tables in the schema should be deleted.
I already have all the table names that I fetched with the code below, but how do delete all those tables?
The following is some psycopg2 code, and below that is the SQL generated
writeCon.execute("SELECT table_name FROM information_schema.tables WHERE table_schema='mySchema'")
SELECT table_name FROM information_schema.tables WHERE table_schema='mySchema'

You can use an anonymous code block for that.
WARNING: This code is playing with DROP TABLE statements, and they are really mean if you make a mistake ;) The CASCADE option drops all depending objects as well. Use it with care!
DO $$
DECLARE
row record;
BEGIN
FOR row IN SELECT * FROM pg_tables WHERE schemaname = 'mySchema'
LOOP
EXECUTE 'DROP TABLE mySchema.' || quote_ident(row.tablename) || ' CASCADE';
END LOOP;
END;
$$;
In case you want to drop everything in your schema, including wrappers, sequences, etc., consider dropping the schema itself and creating it again:
DROP SCHEMA mySchema CASCADE;
CREATE SCHEMA mySchema;

For a single-line command, you can use psql and its \gexec functionality:
SELECT format('DROP TABLE %I.%I', table_schema, table_name)
FROM information_schema.tables
WHERE table_schema= 'mySchema';\gexec
That will run the query and execute each result string as SQL command.

Related

Oracle SQL only drop a column if a table exists

For Microsoft SQL Server I have following statement to only drop a column if the table exist.
IF EXISTS(SELECT 1
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'TEST_TABLE')
ALTER TABLE TEST_TABLE DROP COLUMN LEGACY_VALUE
GO
I was wondering if there was a related IF-EXISTS mechanism is present in Oracle.
All the metadata about the columns in Oracle Database is accessible using one of the following views.
user_tab_cols; -- For all tables owned by the user
all_tab_cols ; -- For all tables accessible to the user
dba_tab_cols; -- For all tables in the Database.
So, if you are looking for a column that exists and want to drop it, your code may look something like this ( see below).
Since this appears to be a one time task, is the effort really worth it?
DECLARE
v_column_exists number := 0;
BEGIN
Select count(*) into v_column_exists
from user_tab_cols
where upper(column_name) = 'LEGACY_VALUE''
and upper(table_name) = 'TEST_TABLE';
--and owner = 'SCOTT --*might be required if you are using all/dba views
if (v_column_exists = 1) then
execute immediate 'alter table test_table drop column legacy_value';
end if;
end;
/

Drop all tables in a Redshift schema - without dropping permissions

I would be interested to drop all tables in a Redshift schema. Even though this solution works
DROP SCHEMA public CASCADE;
CREATE SCHEMA public;
is NOT good for me since that it drops SCHEMA permissions as well.
A solution like
DO $$ DECLARE
r RECORD;
BEGIN
-- if the schema you operate on is not "current", you will want to
-- replace current_schema() in query with 'schematodeletetablesfrom'
-- *and* update the generate 'DROP...' accordingly.
FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = current_schema()) LOOP
EXECUTE 'DROP TABLE IF EXISTS ' || quote_ident(r.tablename) || ' CASCADE';
END LOOP;
END $$;
as reported in this thread How can I drop all the tables in a PostgreSQL database?
would be ideal. Unfortunately it doesn't work on Redshift (apparently there is no support for for loops).
Is there any other solution to achieve it?
Run this SQL and copy+paste the result on your SQL client.
If you want to do it programmatically you need to built little bit code around it.
SELECT 'DROP TABLE IF EXISTS ' || tablename || ' CASCADE;'
FROM pg_tables
WHERE schemaname = '<your_schema>'
I solved it through a procedure that deletes all records. Using this technique to truncate fails but deleting it works fine for my intents and purposes.
create or replace procedure sp_truncate_dwh() as $$
DECLARE
tables RECORD;
BEGIN
FOR tables in SELECT tablename
FROM pg_tables
WHERE schemaname = 'dwh'
order by tablename
LOOP
EXECUTE 'delete from dwh.' || quote_ident(tables.tablename) ;
END LOOP;
RETURN;
END;
$$ LANGUAGE plpgsql;
--call sp_truncate_dwh()
In addition to demircioglu's answer, I had to add Commit after every drop statement to drop all tables in my schema. SELECT 'DROP TABLE IF EXISTS ' || tablename || ' CASCADE; COMMIT;' FROM pg_tables WHERE schemaname = '<your_schema>'
P.S.: I do not have required reputation to add this note as a comment and had to add as an answer.
Using Python and pyscopg2 locally on my PC I came up with this script to delete all tables in schema:
import psycopg2
schema = "schema_to_be_deleted"
try:
conn = psycopg2.connect("dbname='{}' port='{}' host='{}' user='{}' password='{}'".format("DB_NAME", "DB_PORT", "DB_HOST", "DB_USER", "DB_PWD"))
cursor = conn.cursor()
cursor.execute("SELECT tablename FROM pg_tables WHERE schemaname = '%s'" % schema)
rows = cursor.fetchall()
for row in rows:
cursor.execute("DROP TABLE {}.{}".format(schema, row[0]))
cursor.close()
conn.commit()
except psycopg2.DatabaseError as error:
logger.error(error)
finally:
if conn is not None:
conn.close()
Replace correctly values for DB_NAME, DB_PORT, DB_HOST, DB_USER and DB_PWD to connect to the Redshift DB
The following recipe differs from other answers in the regard that it generates one SQL statement for all tables we're going to delete.
SELECT
'DROP TABLE ' ||
LISTAGG("table", ', ') ||
';'
FROM
svv_table_info
WHERE
"table" LIKE 'staging_%';
Example result:
DROP TABLE staging_077815128468462e9de8ca6fec22f284, staging_abc, staging_123;
As in other answers, you will need to copy the generated SQL and execute it separately.
References
|| operator concatenates strings
LISTAGG function concatenates every table name into a string with a separator
The table svv_table_info is used because LISTAGG doesn't want to work with pg_tables for me. Complaint:
One or more of the used functions must be applied on at least one user created tables. Examples of user table only functions are LISTAGG, MEDIAN, PERCENTILE_CONT, etc
UPD. I just now noticed that SVV_TABLE_INFO page says:
The SVV_TABLE_INFO view doesn't return any information for empty tables.
...which means empty tables will not be in the list returned by this query. I usually delete transient tables to save disk space, so this does not bother me much; but in general this factor should be considered.

How to change Null constraint for all columns ?

I have some big tables (30+ columns) with NOT NULL constraints. I would like to change all those constraints to NULL. To do it for a single column I can use
ALTER TABLE <your table> MODIFY <column name> NULL;
Is there a way to do it for all columns in one request ? Or should I copy/paste this line for all columns (><) ?
Is there a way to do it for all columns in one request ?
Yes. By (ab)using EXECUTE IMMEDIATE in PL/SQL. Loop through all the columns by querying USER_TAB_COLUMNS view.
For example,
FOR i IN
( SELECT * FROM user_tab_columns WHERE table_name = '<TABLE_NAME>' AND NULLABLE='N'
)
LOOP
EXECUTE IMMEDIATE 'ALTER TABLE <TABLE_NAME> MODIFY i.COLUMN_NAME NULL';
END LOOP;
In my opinion, by the time you would write the PL/SQL block, you could do it much quickly by using a good text editor. In pure SQL you just need 30 queries for 30 columns.
For a single table you can issue a single alter table command to set the listed columns to allow null, which is a little more efficient than running one at a time, but you still have to list every column.
alter table ...
modify (
col1 null,
col1 null,
col3 null);
If you were applying not null constraints then this would be more worthwhile, as they require a scan of the table to ensure that no nulls are present, and (I think) an exclusive table lock.
You can query user_tab_cols and combine it with a FOR cursor & EXECUTE IMMEDIATE to modify all not null columns - the PL/SQL block for doing like that would look like so:
DECLARE
v_sql_statement VARCHAR2(2000);
BEGIN
FOR table_recs IN (SELECT table_name, column_name
FROM user_tab_cols
WHERE nullable = 'N') LOOP
v_sql_statement :=
'ALTER TABLE ' || table_recs.table_name || ' MODIFY ' || table_recs.column_name || ' NULL';
EXECUTE IMMEDIATE v_sql_statement;
END LOOP;
END;
If you want to do it for all columns in a database instead of the ones in current schema, you can replace user_tab_cols and put in dba_tab_cols; I'd just run the query in the FOR to ensure that the columns being fetched are indeed the correct ones to be modified

Rename mixed-case tables in a single query

In my PostgreSQL 9.2 database there are many tables having name with mixed cases,For example
Tbl_Sales,Tbl_Purch,Tbl_logMnth
What I want to do is
alter table "Table1" rename to table1
but how to rename all mixed-case tables in my database in an easy way ?
this is the query to use
ALTER TABLE name
RENAME TO new_name
Use the following select to get the table(s) with mixed-cases in name
SELECT table_name ucase,lower(table_name) lcase
FROM information_schema.tables
where table_type = 'BASE TABLE' and
table_schema = 'public' and
table_name ~ E'^[[:upper:]][^[:upper:]]'
PostgreSQL string function lower and information_schema.tables
and use PL/PGSQL SQL - DO to rename all tables that have mixed-case
do
$$
declare
rw record;
begin
for rw in
SELECT 'ALTER TABLE "'||t.ucase||'" RENAME to '||t.lcase||';' execme from (
SELECT table_name ucase, lower(table_name) lcase
FROM information_schema.tables
where table_type = 'BASE TABLE' and
table_schema = 'public' and
table_name ~ E'^[[:upper:]][^[:upper:]]')t
loop
execute rw.execme ;
end loop;
end;
$$
Your best bet would probably be to generate a series of dynamic SQL statements, in, say, Python or similar by querying the pg_tables catalog.
You could then iterate over that list for the schemas in which you're interested (or all of them if you want to do that, so long as they're user-created -- you should avoid any of the pg_ ones, among others, as those are managed by Postgres itself) and check to see if the lowercase name is the same as the current name. If they are different, you can then generate the ALTER TABLE statement needed to rename the table, and then execute it, making sure to COMMIT your changes.
sqlfiddle with an example of this.
Note how the foo table I created is listed in there.
I'd recommend white-listing by schema, say, public and any others you want. In the fiddle, for example, you wouldn't want to touch anything in the pg_catalog nor information_schema schemas. You could also filter by tableowner -- you would probably want to avoid, for example, anything owned by the postgres user.
Also, note that when creating tables, casing doesn't matter. If a table foo already exists, and I then try to create Foo, the error ERROR: relation "foo" already exists will result.

Create or replace table in Oracle pl/sql

I need a script which creates table or if it already exist drops it, and when recreates table. After some research I have found out that CREATE OR REPLACE TABLE in pl/sql doesn't exist. So I come up with this script :
DECLARE
does_not_exist EXCEPTION;
PRAGMA EXCEPTION_INIT (does_not_exist, -942);
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE foobar';
EXCEPTION
WHEN does_not_exist
THEN
NULL;
END;
/
CREATE TABLE foobar (c1 INT);
Is there any proper way to achieve this functionality?
You really shouldn't be doing this in PL/SQL, tables created at runtime would be indicative of a flaw in your data model. If you're really convinced you absolutely have to do this then investigate temporary tables first. Personally, I'd reassess whether it's necessary at all.
You seem to be going for the EAFP as opposed to LBYL approach, which is described in a few answers to this question. I would argue that this is unnecessary. A table is a fairly static beast, you can use the system view USER_TABLES to determine whether it exists before dropping it.
declare
l_ct number;
begin
-- Determine if the table exists.
select count(*) into l_ct
from user_tables
where table_name = 'THE_TABLE';
-- Drop the table if it exists.
if l_ct = 1 then
execute immediate 'drop table the_table';
end if;
-- Create the new table it either didn-t exist or
-- has been dropped so any exceptions are exceptional.
execute immediate 'create table the_table ( ... )';
end;
/
Using a global temporary table would seem to be a better option. However, if you insist on dropping and re-adding tables at runtime you could query one of the _TABLES views (i.e. USER_TABLES, DBA_TABLES, ALL_TABLES) to determine if the table exists, drop it if it does, then create it:
SELECT COUNT(*)
INTO nCount
FROM USER_TABLES
WHERE TABLE_NAME = 'FOOBAR';
IF nCount <> 0 THEN
EXECUTE IMMEDIATE 'DROP TABLE FOOBAR';
END IF;
EXECUTE IMMEDIATE 'CREATE TABLE FOOBAR(...)';
Share and enjoy.