Creating a table if it doesn't exist already - sql

I'm trying to create a table if it doesn't exist already. I'm currently checking to see if it exists in DBA_TABLES first and if that query returns nothing then insert. Is there a way to just check in the same statement so I don't have to break it up into separate queries?
This is what I have currently.
BEGIN
SELECT COUNT(*)
INTO lvnTableExists
FROM DBA_TABLES
WHERE Table_Name = 'SOME_TABLE';
IF lvnTableExists = 0 THEN
EXECUTE IMMEDIATE 'CREATE TABLE SOME_TABLE AS (SELECT * FR0M OTHER_TABLE)';
END IF;
END;
This is something that I'm going for.
DECLARE
sql VARCHAR2(100000);
BEGIN
sql := 'CREATE TABLE SOME_TABLE ' ||
'AS (SELECT * FROM OTHER_TABLE) ' ||
'WHERE NOT EXISTS (SELECT NULL ' ||
'FROM DBA_OBJECTS d WHERE d.Object_Name = 'SOME_TABLE' AND ' ||
'd.Owner = 'SOME_TABLE')';
EXECUTE IMMEDIATE sql;
END;
The problem is that, you can't put a WHERE NOT EXISTS in a CREATE TABLE AS statement.

Yes, that's really a shame that Oracle doesn't have that functionality. I'm sure it will come some day. Until then, if you want to write a PL/SQL wrapper, why not do it like that:
DEClARE
table_exists_already exception;
pragma exception_init(table_exists_already, -955 );
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE SOMETABLE...';
EXCEPTION WHEN table_exists_already THEN
DBMS_OUTPUT.PUT_LINE('Table sometable already exists');
END;
/

Related

Using variables in oracle as table names for insert command oracle

Trying to create a procedure that will either insert or update a certain table that its name is stored in another table with more info.
CREATE OR REPLACE PROCEDURE LIMPAR_TAB_proc IS
--stmt VARCHAR2(1000);
n_tab sii_bck_cfg_tab.nome_tab%type;
prefix sii_bck_cfg_tab.pref_tab_bck%type;
max_reg sii_bck_cfg_tab.max_reg_bck%type;
id_fk sii_bck_cfg_tab.id_bck_cfg_tab%type;
n_tab2 sii_bck_tab.nome_tab%type;
testes VARCHAR2(500);
CURSOR c1 IS
SELECT ID_BCK_CFG_TAB,Nome_tab, pref_tab_bck, max_reg_bck FROM
sii_bck_cfg_tab WHERE desativado_em IS NULL OR desativado_em<=SYSDATE AND
n_dias_reten>0 ORDER BY criado_em;
CURSOR c2 IS
SELECT sii_bck_tab.ID_BCK_CFG_TAB , sii_bck_tab.nome_tab from
sii_bck_tab,sii_bck_cfg_tab WHERE
sii_bck_cfg_tab.id_bck_cfg_tab=sii_bck_tab.id_bck_cfg_tab and dt_fecho is
NULL ;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO id_fk,n_tab,prefix,max_reg;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('Nome Tabela = ' || id_fk ||' '|| n_tab ||' '|| prefix
||' '|| max_reg);
OPEN c2;
LOOP
FETCH c2 INTO id_fk, n_tab2;
EXIT WHEN c2%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('chave aqui = ' || id_fk || n_tab2);
IF c2%FOUND THEN
testes:= 'INSERT INTO ' || n_tab2 || 'select * from ' || n_tab;
EXECUTE IMMEDIATE testes;
END IF;
END LOOP;
CLOSE c2;
END LOOP;
CLOSE c1;
so i will try to explain my final objective, i want to go through my cursor1 and when i find a table that passes through the verification then i go into my cursor2. During my second loop i will want to verify if there is a table associated with a table on my cursor1 (not implemented ), then if i find one that is not associated i will need to create one with the same fields as the original(which is why im trying to save the table names in a variable). In case it exists and its dt_fim(date end) is null then i will need to insert all the data from the table from cursor1 (n_tab) into the table found on cursor2(n_tab2).
I will try to explain any doubts further, its still confusing to me, just getting started.
Thank you for any advice/help.
This is the right syntax, but not recommended for such a simple operation
testes:= 'INSERT INTO ' || n_tab2 ||' SELECT * FROM ' || n_tab;
EXECUTE IMMEDIATE testes;
Because It is preferable(and safer) to explicitly specify the column names in an insert, for which you need extra blocks if you want to do it dynamically.
INSERT INTO tab2(col1,col2,col3) SELECT col1,col2,col3 FROM tab;
By the way, any reason why you've put table names in variable instead of doing a direct insert?
Try this below block to pass tablename as variables:
declare
table_1 varchar2(10):='N_tab';
table_2 varchar2(10):='N_tab2';
test varchar2(1000);
begin
test:= 'INSERT all into ' || table_2 || ' SELECT * FROM ' ||table_1;
EXECUTE IMMEDIATE test;
dbms_output.put_line (test);
end;

ORACLE SQL: Looping over table

I have a table that contains a list of table names.
I would like to search each of these tables one by one to see if they contain a particular element (the primary key, specified at the start of the script).
I would like to return a list of all of the tables that this element is present in (ideally distinct).
I'm fairly new to this PL/SQL "not just a query" stuff. so i apologise in advance for the attrocious attempt you are about to see, but hopefully it illustrates what i'm going for:
PROCEDURE CHECK_FOR_ELEMENTS
BEGIN
DECLARE
ELEMENT_KEY varchar(5):=X78ehryfk;
RNUM_MAX int :=167;
----create output table for script
create or replace table ALL_TABLES CONTAINING_&ELEMENT_KEY
(ELEMENT_KEY VARCHAR(255),
TABLE_NAME varchar(255))
/
commit;
---begin loop over rnum;
FOR rnum_counter in 1..&RNUM_MAX
LOOP
--define this statement as variable TABLE_NAME_VAR
select table_name from (select * from (select table_name, rownum as rnum
from all_tables
where owner = 'RMS'
and table_name like 'ABC%'
and table_name not like '%STG'
and table_name not like '%BKP'
and num_rows>0
order by num_rows desc)
where rnum = rnum_counter
)INTO TABLE_NAME_VAR
;
----run below to collect row, if it exists, from table being searched
SQL_STMT:='INSERT INTO ALL_TABLES CONTAINING_&ELEMENT_KEY
SELECT distinct key,'||TABLE_NAME_VAR||' as UMF from
'||TABLE_NAME_VAR||
' where key like 'ELEMENT_KEY-%'
execute immediate SQL_STMT;
commit;
---insert row into table created for output
END LOOP
---loop over all tables
END;
The main error message i get is that TABLE_NAME_VAR is not a valid table name within the dynamic SQL statement. I've googled a bit and i now understand you can't use variables to input table names in this way.
Any help is greatly appreciated!
Thankyou!
Here, I tried to clean it up for you. Let me know if you still get errors.
create or replace PROCEDURE CHECK_FOR_ELEMENTS is
ELEMENT_KEY varchar2(14):='X78ehryfk';
RNUM_MAX int :=167;
TABLE_NAME_VAR varchar2(30);
SQL_STMT varchar2(4000);
BEGIN
----create output table for script
begin
execute immediate 'drop table ALL_TABLES_WITH_' || element_key;
exception when others then null;
end;
execute immediate 'create table ALL_TABLES_WITH_' || element_key || '
(ELEMENT_KEY VARCHAR2(255), -- does this need to be 255 characters?
TABLE_NAME varchar2(30))';
--- implicit cursor loop
FOR rnum_row in (select table_name, rownum as rnum
from all_tables
where owner = 'RMS'
and table_name like 'ABC%'
and table_name not like '%STG'
and table_name not like '%BKP'
and num_rows>0
order by num_rows desc)
LOOP
if rnum_row.rnum > RNUM_MAX
then exit;
end if;
TABLE_NAME_VAR := rnum_row.table_name;
----run below to collect row, if it exists, from table being searched
SQL_STMT:='INSERT INTO ALL_TABLES_WITH_' || element_key || '
(ELEMENT_KEY, TABLE_NAME)
SELECT distinct key, :1 as UMF from
'||TABLE_NAME_VAR||
' where key like :2';
execute immediate SQL_STMT using TABLE_NAME_VAR, element_key || '-%';
---insert row into table created for output
END LOOP;
commit;
---loop over all tables
END CHECK_FOR_ELEMENTS;
/

Get all ids in a database

I want to get all the ids in a database from all columns that have an ID field.
My script so far is:
BEGIN
FOR tname IN (select table_name from all_tab_columns where column_name = 'ID' and owner='PACC_USER') LOOP
EXECUTE IMMEDIATE
'select unique id from ' || tname;
END LOOP;
End;
I get the error PLS-00306: wrong number or types of arguments in call to '||'. What is the problem exactly? Any help is welcomed :)
In your code tname is a record for referencing the cursor result set i.e. a namespace not an attribute. Fix it like this:
BEGIN
FOR tname IN (select table_name
from all_tab_columns
where column_name = 'ID' and owner='PACC_USER')
LOOP
EXECUTE IMMEDIATE
'select unique id from ' || tname.table_name ;
END LOOP;
End;
One would hope that a column called ID would return unique rows without needing the unique keyword but we live in troubled times.
Your code needs to select results into something: PL/SQL is not T-SQL, it requires target variables. So let's improve your code a bit more.
declare
ids_nt sys.dbms-debug_vc2coll;
BEGIN
FOR tname IN (select table_name
from all_tab_columns
where column_name = 'ID' and owner='PACC_USER')
LOOP
EXECUTE IMMEDIATE
'select unique id from ' || tname.table_name
bulk collect into ids_nt;
dbms_output.put_line('IDS for table '|| tname.table_name);
for idx in ids_nt.first() .. ids_nt.last loop
dbms_output.put_line(ids_nt(idx));
end loop;
END LOOP;
End;
Maybe this isn't the kind of thing you want to do with the IDs. If so please edit your question to clarify your intent.
Select column_name,table name from all_tab_column where upper(column_name)=upper('Id') ;

Oracle SQL - If Exists, Drop Table & Create

Can some one please guide me what's wrong with this query? In SQL Server we just check the presence of the Object_ID of a table to drop it and re-create it. I am new to Oracle and wrote this query:
declare Table_exists INTEGER;
BEGIN
Select count(*) into Table_exists from sys.all_tables where table_name='TABLENAME1';
EXCEPTION
WHEN NO_DATA_FOUND
THEN
Table_Exists :=0;
if(table_exists)=1
Then
Execute Immediate 'Drop Table TABLENAME1;'
'Create Table TABLENAME1;';
DBMS_OUTPUT.PUT_LINE('Table Dropped and Re-Created!');
Else
Execute Immediate 'Create Table TABLENAME1;';
DBMS_OUTPUT.PUT_LINE('New Table Created!');
END IF;
END;
I get the output - ANONYMOUS BLOCK COMPLETED, but the table is not created. The table was previously existing, so I dropped it to check if the PL/SQL is actually creating the table, but NO. What is wrong here? What am I missing? Please guide.
When you are using all_tables filter the results for your
schema by adding where owner = 'your_schema'
or use sys.user_tables
ALL_TABLES describes the relational tables accessible to the current user
USER_TABLES describes the relational tables owned by the current user.
When use execute_emmidiate remove the ; from the query;
Modified query;
DECLARE
Table_exists INTEGER;
BEGIN
Select count(*) into Table_exists from sys.user_tables where table_name='TABLENAME1';
--or
--Select count(*) into Table_exists from sys.all_tables
--where table_name='TABLENAME1' and owner = 'your_DB';
if table_exists = 1 Then
Execute Immediate 'Drop Table TABLENAME1';
Execute Immediate 'Create Table TABLENAME1(num number)';
DBMS_OUTPUT.PUT_LINE('Table Dropped and Re-Created!');
Else
Execute Immediate 'Create Table TABLENAME1(num number)';
DBMS_OUTPUT.PUT_LINE('New Table Created!');
END IF;
END;
First note:
Select count(*) into Table_exists
from sys.all_tables
where table_name = 'TABLENAME1';
will always return one row. You don't need the exception handling.
My best guess is that you have more than one table called TABLENAME1. Run this query to find out:
Select *
from sys.all_tables
where table_name = 'TABLENAME1';
Oracle stores tables from all owners that you can access. You might also want to check OWNER_NAME in the where clause.
However, you seem to understand exception handling. So, just drop the table, ignore any errors, and then recreate the table.
The EXCEPTION clause lasts till the next END and not just the next statement. If you want to continue after catching the exception you need to add an additional BEGIN/END:
declare
Table_exists INTEGER;
BEGIN
BEGIN
Select count(*) into Table_exists from sys.all_tables where table_name='TABLENAME1';
EXCEPTION
WHEN NO_DATA_FOUND THEN
Table_Exists :=0;
END;
if(table_exists)=1 Then
Execute Immediate 'Drop Table TABLENAME1;'
Execute Immediate 'Create Table TABLENAME1;';
DBMS_OUTPUT.PUT_LINE('Table Dropped and Re-Created!');
Else
Execute Immediate 'Create Table TABLENAME1;';
DBMS_OUTPUT.PUT_LINE('New Table Created!');
END IF;
END;
As pointed out by Gordon, the EXCEPTION clause is not really needed in this case since count(*) will always return one row. So the following is sufficient:
declare
Table_exists INTEGER;
BEGIN
Select count(*) into Table_exists from sys.all_tables where table_name='TABLENAME1';
if(table_exists)=1 Then
Execute Immediate 'Drop Table TABLENAME1;'
Execute Immediate 'Create Table TABLENAME1;';
DBMS_OUTPUT.PUT_LINE('Table Dropped and Re-Created!');
Else
Execute Immediate 'Create Table TABLENAME1;';
DBMS_OUTPUT.PUT_LINE('New Table Created!');
END IF;
END;

Oracle SQL: Update with variable table name and fixed column name

I am a beginner with SQL and have a problem:
I have a DB with a big number of tables. Some of the tables have a column with the name "lab".
In this colums are values I need to be changed.
So I managed to get the names of the tables via
SELECT CNAME,TNAME FROM SYSTEM.COL WHERE CNAME = 'LAB';
And I know my update command
update TNAME set LAB='VALUE' WHERE LAB='OLDVALUE'
But I can not manage to connect both statements via a variable TNAME or something. I tried to use execute immediate, but that did me no good.
If its Oracle, something like this should do it:
BEGIN
FOR cur_tabs_cols IN ( SELECT CNAME,TNAME FROM SYSTEM.COL WHERE CNAME = 'LAB'; )
LOOP
EXECUTE IMMEDIATE 'UPDATE ' || cur_tabs_cols.TNAME || ' SET LAB = ''VALUE'' WHERE LAB = ''OLDVALUE''';
END LOOP;
COMMIT;
END;
You would need to write pl/sql for this.
The first thing is, please don't use SYSTEM.COL. Instead use the data dictionary view USER_TAB_COLS or USER_TAB_COLUMNS. (or ALL_TAB_COLS if in other schema)
EXECUTE IMMEDIATE would be what you want here.
BEGIN
FOR i IN (SELECT table_name
FROM user_tab_cols
WHERE column_name = 'LAB')
LOOP
EXECUTE IMMEDIATE
'UPDATE ' || i.table_name || ' set LAB = :value where LAB = :oldvalue'
USING 'value', 'oldvalue';
END LOOP;
END;
You can (and should) use a bind variable for value and oldvalue, just not for the table name.