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;
/
Related
Can anyone tell me a query to DDL of all tables under schema in database.
I know how to get a DDL of a table.
select get_ddl('table', 'ods.users');
It gives us only one table DDL. But in my I have around 40 tables.
I want to get all tables DDL at a time instead of getting one by one. Is there any query available. If so please guide me.
Regards,
Kathija.
GET_DDL can script entire schema(with all objects inside):
select get_ddl('SCHEMA', 'ods');
select get_ddl('SCHEMA', 'ods', true); -- to get qualified names
With Snowflake Scripting block it is possible to list over tables/views/functions/... only:
DECLARE
CUR CURSOR FOR SELECT CONCAT_WS('.',TABLE_CATALOG,TABLE_SCHEMA,TABLE_NAME) AS name
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'
AND TABLE_SCHEMA ILIKE 'ODS';
BEGIN
CREATE OR REPLACE TEMPORARY TABLE temp_view_defs(view_name TEXT, definition TEXT);
FOR rec IN CUR DO
EXECUTE IMMEDIATE REPLACE('INSERT INTO temp_view_defs(view_name, definition)
SELECT ''<view_name>'', GET_DDL(''TABLE'', ''<view_name>'')'
,'<view_name>'
,rec.name);
END FOR;
LET rs RESULTSET := (SELECT * FROM temp_view_defs);
RETURN TABLE(rs);
END;
For sample:
CREATE SCHEMA ODS;
CREATE TABLE ODS.TAB1(i INT);
CREATE TABLE ODS.TAB2(id INT DEFAULT 0) COMMENT = 'Test';
Output:
I have a table called schema.exampletab, which has partitions where the partition name is on the form Pxxxxxx where the x's are partition periods (year and month YYYYMM) and then there is a local PK. I want to drop a partition, but I get the error message ORA-14006: Invalid partition name when attempting to drop it, but when I try to simply query data from a partition it selects and displays the data from the partition in question. Note that I am using a scheduler which uses date as a parameter using YYYYMMDD, so I usually substring.
For instance I can query data by using the following statement:
SELECT *
FROM schema.exampletab
PARTITION (P202110);
This returns 20 rows of data in my table.
I then try to drop the partition using the following statement:
ALTER TABLE schema.exampletab DROP PARTITION (concat(P,substr('20211011',1,6))
UPDATE INDEXES;
This however, leads to the ORA-14006 error message. In order to control that the partition exists I tried to look up that the schema, table, and partition exists in the all_tab_partitions where all partitions in the database I work on are logged.
select partition_name from all_tab_partitions
where table_owner = 'schema' and table_name = 'exampletab' and
substr(partition_name,2,7) = substr('20211022',1,6);
This returns the partition name P202110 in the query result.
I hoped I could use the drop partition with a subquery like this:
ALTER TABLE schema.exampletab DROP PARTITION select partition_name from all_tab_partitions
where table_owner = 'schema' and table_name = 'exampletab' and
substr(partition_name,2,7) = substr('20211022',1,6);
However, this still leads to ORA-14006 error. I have tried to write the partition name like 'P202110' and P202110 instead of the parenteces, but no luck.
How can I write the drop partition statement such that it drops the partition instead of giving the ORA-14006 error?
This is something which I have to do routinely and so it would be nice to know how to properly select and drop partitions, or truncate them, etc. Also, I use a scheduler which runs the sql queries at specified intervals so there I have to specify dates as a parameter, meaning that in my example code the YYYYMM is a parametervalue that gets parsed in and so I need to concactinate the P with this outputted parameter value, because if I type only P202110 it actually drops it.
You have two options, keeping in consideration that you are using sysdate to know which partition you should drop.
Option 1 -> Dynamic SQL and the result must be executed outside the query
select ' alter table '||table_owner||'.'||table_name||' drop partition '||partition_name||' update indexes ; '
from
dba_Tab_partitions
where table_owner = 'your_schema'
and table_name = 'your_table'
and partition_name = 'P'||substr(to_char(sysdate,'yyyymmdd'),1,6) ; ;
This query gives you the command output, but you need to execute it
alter table yourschema.yourtable drop partition P202110 update indexes;
Example in my own environment
SQL> select ' alter table '||table_owner||'.'||table_name||' drop partition '||partition_name||' update indexes ; '
from
dba_Tab_partitions
where table_owner = 'FDM_DATA'
and table_name = 'FDM_DIM_CUSTOMER'
and partition_name = 'P_'||substr(to_char(sysdate,'yyyymmdd'),1,6) ;
'ALTERTABLE'||TABLE_OWNER||'.'||TABLE_NAME||'DROPPARTITION'||PARTITION_NAME||'UP
--------------------------------------------------------------------------------
alter table FDM_DATA.FDM_DIM_CUSTOMER drop partition P_202110 update indexes ;
Option 2 -> PLSQL
A better option is to use PLSQL. A small example when you want to drop only one partition based on the current sysdate. You can extend / modify this code to cover any kind of time frame.
declare
v_owner varchar2(128) := 'YOUR_SCHEMA';
v_table_name varchar2(128) := 'YOUR_TABLE';
v_partition_name varchar2(128);
begin
select partition_name into v_partition_name from all_tab_partitions
where table_owner = v_owner and
table_name = v_table_name and
partition_name = 'P'||substr(to_char(sysdate,'yyyymmdd'),1,6) ;
execute immediate 'alter table '||v_owner||'.'||v_table_name||' drop partition '||v_partition_name||' update indexes' ;
exception
when no_data_found then null; -- if there is no partition, nothing to do and no error is raised
when others then raise;
end;
/
You cannot use "expressions" in partition name
This is wrong:
ALTER TABLE schema.exampletab DROP PARTITION (concat(P,substr('20211011',1,6))
UPDATE INDEXES;
this is correct:
ALTER TABLE schema.exampletab DROP PARTITION P20211011
UPDATE INDEXES;
You may use this code to drop partitions dynamically. Just update C_OWNER ,C_TABLE_NAME , C_PARTITION_TEMPLATE in the header. Please pay attention that the sql command is built dynamically, but when it is ready is has the partition name fully parsed.
declare
C_OWNER varchar2(128) := 'MYOWNER';
C_TABLE_NAME varchar2(128) := 'MYTABLE';
C_PARTITION_TEMPLATE varchar2(128) := '2011';
cursor part_cur is
select *
from all_tab_partitions
where table_owner=C_OWNER and
table_name = C_TABLE_NAME and
partition_name like '%'||C_PARTITION_TEMPLATE||'%'
order by partition_position;
BEGIN
for part_rec in part_cur loop
execute immediate 'ALTER TABLE "'||part_rec.table_owner||'"."'||part_rec.table_name||'"'||
' DROP PARTITION ("'||part_rec.partition_name||'") UPDATE INDEXES';
end loop;
END;
/
How can I drop multiple tables in the same database that starts with same prefix?
Ex:
Query to delete 1 table
drop table project_id.db.test_table_<some_random_string>
But how can I drop all tables that start with the same prefix test_table_ in the same db?
A possible work around would be. (region is set eu)
BEGIN
DECLARE drop_statments ARRAY<string>;
DECLARE len int64 default 1;
SET drop_statments = (SELECT ARRAY_AGG( 'drop table ' || table_schema ||'.' || table_name)
FROM `region-eu.INFORMATION_SCHEMA.TABLES`
WHERE table_schema = 'db' and table_name like 'Table_Prefix%'
);
WHILE ARRAY_LENGTH(drop_statments) >= len DO
EXECUTE IMMEDIATE drop_statments[offset(len-1)];
SET len = len +1;
END WHILE ;
END;
You may use any of below INFORMATION_SCHEMA dataset.
-- Returns metadata for tables in a single dataset.
SELECT * FROM myDataset.INFORMATION_SCHEMA.TABLES;
-- Returns metadata for tables in a region.
SELECT * FROM region-us.INFORMATION_SCHEMA.TABLES;
Similar to narendra# solution, but using FOR..IN:
FOR drop_statement IN
(SELECT CONCAT("drop table ",table_schema,".", table_name, ";" ) AS value
FROM dataset.INFORMATION_SCHEMA.TABLES -- or region.INFORMATION_SCHEMA.TABLES
WHERE table_name LIKE "table_prefix%"
ORDER BY table_name DESC)
DO
EXECUTE IMMEDIATE(drop_statement.value); -- Here the table is dropped
END FOR;
It's also worth to mention, that you can change "table" to "view" also depending on the information returned from the INFORMATION_SCHEMA views.
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.
How do I add a simple check before adding a column to a table for an oracle db? I've included the SQL that I'm using to add the column.
ALTER TABLE db.tablename
ADD columnname NVARCHAR2(30);
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 like ADD_TMS in SCOTT.EMP Table and add the column only if it does not exist, the PL/SQL Code would be along these lines..
DECLARE
v_column_exists number := 0;
BEGIN
Select count(*) into v_column_exists
from user_tab_cols
where upper(column_name) = 'ADD_TMS'
and upper(table_name) = 'EMP';
--and owner = 'SCOTT --*might be required if you are using all/dba views
if (v_column_exists = 0) then
execute immediate 'alter table emp add (ADD_TMS date)';
end if;
end;
/
If you are planning to run this as a script (not part of a procedure), the easiest way would be to include the alter command in the script and see the errors at the end of the script, assuming you have no Begin-End for the script..
If you have file1.sql
alter table t1 add col1 date;
alter table t1 add col2 date;
alter table t1 add col3 date;
And col2 is present,when the script is run, the other two columns would be added to the table and the log would show the error saying "col2" already exists, so you should be ok.
Or, you can ignore the error:
declare
column_exists exception;
pragma exception_init (column_exists , -01430);
begin
execute immediate 'ALTER TABLE db.tablename ADD columnname NVARCHAR2(30)';
exception when column_exists then null;
end;
/
Normally, I'd suggest trying the ANSI-92 standard meta tables for something like this but I see now that Oracle doesn't support it.
-- this works against most any other database
SELECT
*
FROM
INFORMATION_SCHEMA.COLUMNS C
INNER JOIN
INFORMATION_SCHEMA.TABLES T
ON T.TABLE_NAME = C.TABLE_NAME
WHERE
C.COLUMN_NAME = 'columnname'
AND T.TABLE_NAME = 'tablename'
Instead, it looks like you need to do something like
-- Oracle specific table/column query
SELECT
*
FROM
ALL_TAB_COLUMNS
WHERE
TABLE_NAME = 'tablename'
AND COLUMN_NAME = 'columnname'
I do apologize in that I don't have an Oracle instance to verify the above. If it does not work, please let me know and I will delete this post.
To check column exists
select column_name as found
from user_tab_cols
where table_name = '__TABLE_NAME__'
and column_name = '__COLUMN_NAME__'
Reference link