Catching an exception while altering a table in Oracle - sql

I'm trying to write a command in Oracle that will wither ADD or MODIFY a column depending on whether or not it already exists. Basically something like:
BEGIN
ALTER TABLE MY_TABLE ADD ( COL_NAME VARCHAR2(100 );
EXCEPTION WHEN OTHERS THEN
ALTER TABLE MY_TABLE MODIFY ( COL_NAME VARCHAR2(100) );
END;
However, Oracle complains about having the ALTER command inside of BEGIN. Is there a way to achieve this using a single SQL command in Oracle?
Thanks!

In order to put DDL in a PL/SQL block, you would need to use dynamic SQL.
Personally, I'd check whether the column exists first and then issue the DDL. Something like
DECLARE
l_cnt INTEGER;
BEGIN
SELECT COUNT(*)
INTO l_cnt
FROM dba_tab_cols
WHERE table_name = 'MY_TABLE'
AND owner = <<owner of table>>
AND column_name = 'COL_NAME';
IF( l_cnt = 0 )
THEN
EXECUTE IMMEDIATE 'ALTER TABLE my_table ADD( col_name VARCHAR2(100) )';
ELSE
EXECUTE IMMEDIATE 'ALTER TABLE my_table MODIFY( col_name VARCHAR2(100) )';
END IF;
END;
If you don't have access to DBA_TAB_COLS, you could also use ALL_TAB_COLS or USER_TAB_COLS depending on what schema the table resides in and what privileges you have on the table.

I found a solution based on this post.
DECLARE v_column_exists number := 0;
BEGIN
SELECT COUNT(*) INTO v_column_exists
FROM ALL_TAB_COLUMNS
WHERE TABLE_NAME = 'MY_TABLE'
AND COLUMN_NAME = 'COL_NAME';
IF (v_column_exists = 0) THEN
EXECUTE IMMEDIATE 'ALTER TABLE MY_TABLE ADD ( COL_NAME VARCHAR2(200) )';
ELSE
EXECUTE IMMEDIATE 'ALTER TABLE MY_TABLE MODIFY ( COL_NAME VARCHAR2(200) )';
END IF;
END;

Related

PL/SQL if table not exist create

Hello i use oracle SQL developer
I have create a procedure, and i need to check if a table exist, if not exist i must create how can do?
I have try this
DECLARE v_emp int:=0;
BEGIN
SELECT count(*) into v_emp FROM dba_tables;
if v_emp = 0 then
EXECUTE IMMEDIATE 'create table EMPLOYEE ( ID NUMBER(3), NAME VARCHAR2(30) NOT NULL)';
end if;
END;
but give me an error 00103 because not find table
Just execute the create and watch the exception if thrown. Oracle would never replace the DDL of a table.
declare
error_code NUMBER;
begin
EXECUTE IMMEDIATE 'CREATE TABLE EMPLOYEE(AGE INT)';
exception
when others then
error_code := SQLCODE;
if(error_code = -955)
then
dbms_output.put_line('Table exists already!');
else
dbms_output.put_line('Unknown error : '||SQLERRM);
end if;
end;
You can run this for example:
if (select count(*) from all_tables where table_name = 'yourTable')>0 then
-- table exists
else
-- table doesn't exist
end if;
You should try following,
declare
nCount NUMBER;
v_sql LONG;
begin
SELECT count(*) into nCount FROM dba_tables where table_name = 'EMPLOYEE';
IF(nCount <= 0)
THEN
v_sql:='
create table EMPLOYEE
(
ID NUMBER(3),
NAME VARCHAR2(30) NOT NULL
)';
execute immediate v_sql;
END IF;
end;

CREATE UNIQUE INDEX IF NOT EXISTS in postgreSQL

Plese I would like to do in PostgreSQL something like
CREATE UNIQUE INDEX IF NOT EXISTS
Any idea?
You can check, if an index with a given name exists with this statement.
If your index name is some_table_some_field_idx
SELECT count(*) > 0
FROM pg_class c
WHERE c.relname = 'some_table_some_field_idx'
AND c.relkind = 'i';
Starting from Postgres 9.5 you can even use
CREATE INDEX IF NOT EXISTS
Just another ready-to-use solution.
PostgreSQL v9.0+:
DO $BLOCK$
BEGIN
BEGIN
CREATE INDEX index_name ON table_name( column_name );
EXCEPTION
WHEN duplicate_table
THEN RAISE NOTICE 'index ''index_name '' on table_name already exists, skipping';
END;
END;
$BLOCK$;
PostgreSQL v9.5+:
CREATE INDEX IF NOT EXISTS index_name ON table_name( column_name );
I have wrapped a_horse_with_no_name's code with PLSQL function for more convenient usage. I hope somebody will find it useful.
CREATE OR REPLACE FUNCTION create_index(table_name text, index_name text, column_name text) RETURNS void AS $$
declare
l_count integer;
begin
select count(*)
into l_count
from pg_indexes
where schemaname = 'public'
and tablename = lower(table_name)
and indexname = lower(index_name);
if l_count = 0 then
execute 'create index ' || index_name || ' on ' || table_name || '(' || column_name || ')';
end if;
end;
$$ LANGUAGE plpgsql;
usage:
select create_index('my_table', 'my_index_name', 'id');
You need some procedural code for this, something like this (untested!):
do
$$
declare
l_count integer;
begin
select count(*)
into l_count
from pg_indexes
where schemaname = 'public'
and tablename = 'your_table'
and indexname = 'your_index_name';
if l_count = 0 then
execute 'create unique index public.your_index_name on public.your_table(id)';
end if;
end;
$$
If you are still stuck in previous versions, I would recommend not using count, but just the query directly in your if condition. Makes the code simpler. You can try something like this:
do
$$
begin
if not exists (
select indexname
from pg_indexes
where schemaname = 'schemaname'
and tablename = 'tablename'
and indexname = 'indexname'
)
then
create unique indexname (...);
end if;
end
$$;
Another solution that support multiple columns index, based on #Kragh answer
CREATE or replace FUNCTION create_index(_index text, _table text, VARIA
DIC param_args text[]) RETURNS void AS
$$
declare
l_count integer;
begin
select count(*) into l_count
from pg_indexes
where schemaname = 'public'
and tablename = lower(_table)
and indexname = lower(_index);
if l_count = 0 then
EXECUTE format('create index %I on %I (%s)', _index, _table, array_to_string($3,','));
end if;
END;
$$
LANGUAGE plpgsql;
and then you can use it like any other pg function:
select create_index('events_timestamp_type_idx', 'events', 'timestamp', 'type');

Checking for the existence of a column in a view before searching for a value

I am trying to search every view in the database for a specific value of something like '%THIS%'
I came up with this plsql code
DECLARE
match_count INTEGER;
BEGIN
FOR t IN (SELECT name FROM user_dependencies where type = 'VIEW') LOOP
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM '|| t.name || ' Where Column_Name LIKE ''%THIS%'' '
INTO match_count;
IF match_count > 0 THEN
dbms_output.put_line( t.name ||' '||match_count );
END IF;
END LOOP;
END;
But when I try to run it, I get an invalid identifier error for the column name in the execute immeadiate query.
The problem to me is obvious that not every view has the Column_Name, but I can't figure out how I can check to see if the column exists before running the query as I loop through all of the views.
I was also able to use a slightly modified version of this to run through all of the tables, and while they do not all have that column, I did not run in to this issue.
Edit: I am including the plsql code that I was able to use to loop through the tables.
DECLARE
match_count INTEGER;
BEGIN
FOR t IN (SELECT table_name, column_name FROM all_tab_columns
where table_name LIKE 'THIS%' and data_type = 'VARCHAR2' AND column_name = 'Column_name') LOOP
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM '||t.table_name || ' Where Column_name LIKE ''%THIS%'' '
INTO match_count;
IF match_count > 0 THEN
dbms_output.put_line( t.table_name ||' '||t.column_name||' '||match_count );
END IF;
END LOOP;
END;
I'm assuming this is Oracle since you tagged pl/sql. You can use Oracle's metadata tables to see what columns a table/view has. Specifically USER_TAB_COLUMNS.
select count(*)
from user_tab_columns
where table_name = [name of view]
and column_name = [name of column]
This should query every view that has a column_name like '%START%'
declare
cursor getViews is
select table_name, column_name from user_tab_cols where table_name in
(select view_name from user_views)
and column_name like '%START%';
myResult number;
BEGIN
for i in getViews
LOOP
execute immediate 'select count(*) from '||i.table_name into myResult;
END LOOP;
END;

Cursor and table cannot be found

I have a procedure that will select MAX from some tables, but for some reason it is not able to find these tables. Could anybody help me?
declare
varible1 varchar2 (255);
temp varchar2 (255);
last_val number(9,0);
cursor c1 is Select distinct table_name from user_tab_cols order by table_name;
begin
FOR asd in c1
LOOP
temp := asd.table_name;
varible1 := '"'||temp||'"';
select max("id") into last_val from varible1 ;
END LOOP;
end;
For example, the first table name is acceptance_form and for select I need to use "acceptance_form".
Code after edit:
declare
varible1 varchar2 (255);
temp varchar2 (255);
last_val number(9,0);
cursor c1 is Select distinct table_name from user_tab_cols where column_name = 'id';
begin
FOR asd in c1
LOOP
temp := asd.table_name;
execute immediate 'select NVL(max('||'id'||'),0) from "'||varible1||'"' into last_val;
END LOOP;
end;
Can't cuz db is Case sensitive Oracle express 10g tables and rows was created like this
CREATE TABLE "ADMINMME"."acceptance_form"
(
"group_id" NUMBER(9, 0),
"id" NUMBER(4, 0) DEFAULT '0' NOT NULL ,
"is_deleted" NUMBER(4, 0),
"name" NVARCHAR2(30) NOT NULL
);
Can u tell me how to handle exception sequence dosn't exist for this;
Nevermind exception was in wrong block :)
declare
temp varchar2 (255);
last_val number(9,0);
cursor c1 is Select distinct table_name from user_tab_cols where column_name = 'id';
begin
FOR asd in c1
LOOP
temp := asd.table_name;
execute immediate 'select NVL(max("id"),0)+1 from "'||temp||'"' into last_val;
begin
EXECUTE IMMEDIATE 'drop sequence "seq_'|| temp||'"';
EXECUTE IMMEDIATE 'create SEQUENCE "seq_'|| temp ||'" MINVALUE '||last_val||'MAXVALUE 999999999999999999999999999 INCREMENT BY 1 NOCACHE';
EXECUTE IMMEDIATE 'select '||temp||'.nextval from dual';
EXECUTE IMMEDIATE 'ALTER SEQUENCE "seq_'||temp||'" INCREMENT BY 1';
exception when others then
null;
end;
END LOOP;
end;
Dynamic sql doesn't work in that way.
declare
varible1 varchar2 (255);
temp varchar2 (255);
last_val number(9,0);
cursor c1 is Select distinct table_name from user_tab_cols order by table_name;
begin
FOR asd in c1
LOOP
temp := asd.table_name;
begin
execute immediate 'select max(id) from '||temp into last_val;
dbms_output.put_line('max(id) for table: ' ||temp||' = '||last_val);
exception when others then
dbms_output.put_line('Failed to get max(id) for table: ' ||temp);
end;
END LOOP;
end;
You can't use a variable for the table name.
What you can do is creating the complete sql statement as a string and use execute immediate
Here are some examples how to do that: http://docs.oracle.com/cd/B19306_01/appdev.102/b14261/dynamic.htm#CHDGJEGD

SQL Variable, How to do it?

I have this SQL:
DROP TABLE MISSINGTABLE;
CREATE TABLE MISSINGTABLE (
TABLE_NAME VARCHAR2 (70),
DESCRIPTION VARCHAR2 (1000)
)
CREATE OR REPLACE PROCEDURE MISSINGTABLES AS
BEGIN
INSERT INTO MISSINGTABLE
((((SELECT TABLE_NAME, 'Missing Table on PEKA_ERP_001' Description FROM ALL_TABLES WHERE OWNER = 'ASE_ERP_001')
MINUS
(SELECT TABLE_NAME, 'Missing Table on PEKA_ERP_001' Description FROM ALL_TABLES WHERE OWNER = 'PEKA_ERP_001'))
UNION
((SELECT TABLE_NAME, 'Missing Table on ASE_ERP_001' Description FROM ALL_TABLES WHERE OWNER = 'PEKA_ERP_001')
MINUS
(SELECT TABLE_NAME, 'Missing Table on ASE_ERP_001' Description FROM ALL_TABLES WHERE OWNER = 'ASE_ERP_001'))));
END;
So, how u can see, I'm creating a Table and then a Procedure, which fills the Table.
Now I want 2 Variables for these Arguments: 'PEKA_ERP_001' and 'ASE_ERP_001' (so I don't always need to write it manually, because this values changes a lot)
I tried this (included only the first part of above Statement):
DECLARE
S1 VARCHAR2(100) := 'ASE_ERP_001';
S2 VARCHAR2(100) := 'PEKA_ERP_001';
TableMissing VARCHAR(100) := 'Missing Table on ';
Apostrophe VARCHAR(10) := '''';
BEGIN
EXECUTE IMMEDIATE ('CREATE OR REPLACE PROCEDURE MISSINGTABLES AS BEGIN INSERT INTO MISSINGTABLE (SELECT TABLE_NAME, ' || Apostrophe || TableMissing || S2 || Apostrophe || ' Description FROM ALL_TAB_COLUMNS WHERE OWNER = ' || Apostrophe || S1 || Apostrophe || ')' || ' END;');
END;
It creates The Procedure, but the Procedure contains the "CREATE OR REPLACE PROCEDURE" itself and its showing me an error... (I cannot execute the Procedure)
Can anyone help me? How can I write the first SQL Statement at the Head which works, only with 2 Variables more, ASE_ERP_001 and PEKA_ERP_001 ?
EDIT:
Statement:
DECLARE
S1 VARCHAR2(100) := 'ASE_ERP_001';
S2 VARCHAR2(100) := 'PEKA_ERP_001';
TabelleFehlt VARCHAR(100) := 'Diese Tabelle fehlt ';
Hochkomma VARCHAR(10) := '''';
BEGIN
EXECUTE IMMEDIATE ('CREATE OR REPLACE PROCEDURE MISSINGTABLES AS BEGIN INSERT INTO MISSINGTABLE (SELECT TABLE_NAME, ' || Hochkomma || TabelleFehlt || S2 || Hochkomma || ' Beschreibung FROM ALL_TAB_COLUMNS WHERE OWNER = ' || Hochkomma || S1 || Hochkomma || ') END;');
END;
The Statement Above Creates a Procedure.
But it also shows me this:
ORA-06512: in Row 7
24344. 00000 - "success with compilation error"
*Cause: A sql/plsql compilation error occurred.
*Action: Return OCI_SUCCESS_WITH_INFO along with the error code
And The PROCEDURE Itselfs Contains this:
create or replace
PROCEDURE MISSINGTABLES AS BEGIN INSERT INTO MISSINGTABLE (SELECT TABLE_NAME, 'Diese Tabelle fehlt PEKA_ERP_001' Beschreibung FROM ALL_TAB_COLUMNS WHERE OWNER = 'ASE_ERP_001') END;
But it should not Contain "Create or Replace Procedure MISSINGTABLES" etc. only the INSERT STatement, I cannot execute the Procedure anyway..
even better would be to use the script from bpgergo, if it would go.
I hope I did not mix the arguments up, you should check them again
CREATE OR REPLACE PROCEDURE MISSINGTABLES (p_1 in varchar2, p_2 in varchar2)
AS
BEGIN
INSERT INTO MISSINGTABLE
((((SELECT TABLE_NAME, 'Missing Table on '||p_1 Description FROM ALL_TABLES WHERE OWNER = p_2)
MINUS
(SELECT TABLE_NAME, 'Missing Table on '||p_1 Description FROM ALL_TABLES WHERE OWNER = p_1))
UNION
((SELECT TABLE_NAME, 'Missing Table on '||p_2 Description FROM ALL_TABLES WHERE OWNER = p_1)
MINUS
(SELECT TABLE_NAME, 'Missing Table on '||p_2 Description FROM ALL_TABLES WHERE OWNER = p_2))));
END;
EDIT
you would call this like:
begin
MISSINGTABLES ('PEKA_ERP_001', 'ASE_ERP_001');
end;
The SQL that you are trying to execute immediate will be evaluated as:
CREATE OR REPLACE PROCEDURE MISSINGTABLES AS
BEGIN
INSERT INTO MISSINGTABLE
(SELECT TABLE_NAME, COLUMN_NAME, 'Missing Table on PEKA_ERP_001' Beschreibung
FROM ALL_TAB_COLUMNS WHERE OWNER = 'ASE_ERP_001')
END;
This probably isn't the logic that you actually want, but the immediate problem is that you are trying to populate a non-existant third column called Beschreibung instead of populating the second column, DESCRIPTION .
Might I suggest an improvement to your SELECT?
Here's a possible alternative:
SELECT
TABLE_NAME,
'Missing Table on'
|| CASE MAX(OWNER) WHEN 'PEKA_ERP_001' THEN 'ASE_ERP_001' ELSE 'PEKA_ERP_001' END
AS Description
FROM ALL_TABLES
WHERE OWNER IN ('PEKA_ERP_001', 'ASE_ERP_001')
GROUP BY TABLE_NAME
HAVING COUNT(*) = 1
This query returns only rows where a TABLE_NAME has just one OWNER. The owner that is missing the table is then shown to be as the other one of the two being tested.
Using parameters, the entire CREATE PROCEDURE statement might look like this:
CREATE OR REPLACE PROCEDURE MISSINGTABLES
(
owner1 IN varchar2,
owner2 IN varchar2
)
AS
BEGIN
INSERT INTO MISSINGTABLE
(
SELECT
TABLE_NAME,
'Missing Table on'
|| CASE MAX(OWNER) WHEN owner1 THEN owner2 ELSE owner1 END
AS Description
FROM ALL_TABLES
WHERE OWNER IN (owner1, owner2)
GROUP BY TABLE_NAME
HAVING COUNT(*) = 1
);
END;