Disable all related constraints from a table with Oracle PL/SQL - sql

CREATE OR REPLACE PROCEDURE disable_all_constraints
(p_owner IN VARCHAR2,
p_table_name IN VARCHAR2)
IS
CURSOR check_status IS
(SELECT status
FROM all_constraints
WHERE owner = p_owner
AND table_name = p_table_name );
v_status VARCHAR2(10);
BEGIN
OPEN check_status;
FETCH check_status
INTO v_status;
IF (check_status %NOTFOUND)
THEN
DBMS_OUTPUT.PUT_LINE('No Constraints added to the table, give me another table to process!');
ELSE
FOR cur IN (SELECT owner, constraint_name , table_name
FROM all_constraints
WHERE owner = p_owner
AND table_name = p_table_name)
LOOP
EXECUTE IMMEDIATE 'ALTER TABLE '||cur.owner||'.'||cur.table_name||' MODIFY CONSTRAINT '||cur.constraint_name||' DISABLE CASCADE ';
END LOOP;
END IF;
CLOSE check_status;
SELECT DISTINCT status
INTO v_status
FROM all_constraints
WHERE owner = p_owner
AND table_name = p_table_name;
IF v_status = 'DISABLED'
THEN
DBMS_OUTPUT.PUT_LINE('All related constraints disable succesufully!');
ELSE
DBMS_OUTPUT.PUT_LINE('Something went wrong, but that is impossible!');
END IF;
END;
So i wrote the above piece. I tested it and it does the job pretty well i think. But my question is, how correct is it or how bad is it? Can it be done in a much easier and optimal way so to speak? Thanks.

There is nothing intrinsically wrong with the way you're doing this but there are a number of potential issues and some unnecessary code.
Your first cursor is unnecessary, you're going to loop through ALL_CONSTRAINTS anyway - if you don't enter the loop then the table has no constraints
You may attempt to disable already disabled constraints - you only need to select those constraints that are currently enabled.
Your final select to see whether there are any non-disabled constraints will always fail if there is more than one status as you'll get a TOO_MANY_ROWS exception (for returning more than one row). This means that your final ELSE statement will never be entered.
It's highly unlikely that a constraint will not be disabled without an exception being raised. I wouldn't even bother with the final check.
More generally, there's rarely a point in outputting information to screen. It requires someone to read it. It's normally better if everything just works, or an exception is raised in the event of an error.
I would simplify this massively to something like the following, which simply loops through the non-disabled constraints and disables them.
create or replace procedure disable_all_constraints (
p_owner in varchar2
, p_table_name in varchar2
) is
begin
for cur in ( select owner || '.' || table_name as object
, constraint_name
from all_constraints
where owner = p_owner
and table_name = p_table_name
and status <> 'DISABLED'
) loop
execute immediate 'alter table ' || cur.object || '
modify constraint ' || cur.constraint_name || '
disable cascade';
end loop;
end;
If you feel like you have to have the extra checks and printing this can be done more cleanly:
create or replace procedure disable_all_constraints (
p_owner in varchar2
, p_table_name in varchar2
) is
l_has_constraint boolean := False;
l_ct number;
begin
for cur in ( select owner || '.' || table_name as object
, constraint_name
from all_constraints
where owner = p_owner
and table_name = p_table_name
and status <> 'DISABLED'
) loop
l_has_constraint := True;
execute immediate 'alter table ' || cur.object || '
modify constraint ' || cur.constraint_name || '
disable cascade';
end loop;
if not l_has_constraint then
dbms_output.put_line('No Constraints added to the table.');
else
select count(*) into l_ct
from all_constraints
where owner = p_owner
and table_name = p_table_name
and status <> 'DISABLED'
;
if l_ct = 0 then
dbms_output.put_line('All related constraints disable successfully');
else
dbms_output.put_line('Something went wrong, but that is impossible');
end if;
end if;
end;

Related

searching a concatenated string in whole database

I am trying to find a concatenated string in Whole Oracle Database Using Below Script
DECLARE
match_count INTEGER;
-- Type the owner of the tables you are looking at
-- v_owner VARCHAR2(255) :='APEX_030200';
-- Type the data type you are look at (in CAPITAL)
-- VARCHAR2, NUMBER, etc.
v_data_type VARCHAR2(255) :='VARCHAR2';
-- Type the string you are looking at
v_search_string VARCHAR2(4000) :='48-S-9-00028';
v_sql varchar2(1000);
BEGIN
FOR t IN (
SELECT table_name, column_name,owner
FROM sys.all_tab_cols
WHERE
data_type LIKE '%CHAR%'
AND owner NOT IN ('XDB','SYS','SYSMAN')
AND table_name <> 'Folder36_TAB'
) LOOP
--v_sql := 'SELECT COUNT(*) FROM '|| t.owner || '.'||t.table_name||' WHERE '||t.column_name || ' like ''%'' || :1 || ''%''';
v_sql := 'SELECT COUNT(*) FROM '|| t.owner || '.'||t.table_name||' WHERE '||t.column_name || ' = :1 ';
EXECUTE IMMEDIATE
v_sql
INTO match_count
USING v_search_string;
IF match_count > 0 THEN
dbms_output.put_line(v_sql);
dbms_output.put_line(t.owner||'.'|| t.table_name ||' '||t.column_name||' '||match_count );
END IF;
END LOOP;
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line(v_sql);
END;
/
The String 48-S-9-00028 is concatenation of 4 Columns
like 48 , S , 9 AND 00028 = 48-S-9-00028
I want to find the concatenated string through whole database that which 4 columns are making this string , I have tried from below scripts but its only giving output for a single value.
If the value being searched is from 4 concatenated cols, then your best bet doing a trawl is to search for each component value, since the value '48-S-9-00028' won't actually exist.
If the the values is obtained from a view with derived values, then you'll be out of luck. All is not lost though, see my comment about using AWR or trace to find what queries are being run.
So, searching for this I would change your trawl to the following (NOTE, this is functional only, might not be quick):
DECLARE
match_count INTEGER;
match_value VARCHAR2(20);
v_data_type VARCHAR2(255) :='VARCHAR2';
v_sql varchar2(1000);
BEGIN
FOR t IN (
SELECT table_name, column_name,owner
FROM sys.all_tab_cols
WHERE
data_type LIKE '%CHAR%'
AND owner NOT IN ('XDB','SYS','SYSMAN')
AND table_name <> 'Folder36_TAB'
) LOOP
v_sql := 'SELECT ''' || t.column_name || ''' cn '
'FROM '|| t.owner || '.'||t.table_name||
' WHERE '||t.column_name || ' IN ( ''48'', ''S'', ''9'', ''00028'' ) ' ||
' AND rownum < 2';
EXECUTE IMMEDIATE v_sql INTO match_value;
dbms_output.put_line(v_sql);
dbms_output.put_line(t.owner||'.'|| t.table_name ||' '||t.column_name||' '||match_value );
END IF;
END;
That is, search for any table where any of the 4 component values exist, return just the value matched and just the first row found to help you narrow your search.
" client have just shown me a report for which we dont have source code , so exactly dont know which 4 columns are being concatenated"
First of all, the client must have some idea of what data this report presents. This could give you some clue to what sort of tables you need to look at.
Secondly, if it's a business key - and if it isn't then why look for it - then just maybe there's a primary key or unique key defined for those columns. So, this query might produce some candidates:
select c.constraint_name, c.constraint_type
from user_cons_columns cc
join user_constraints c
on c.constraint_name = cc.constraint_name
group by c.constraint_name, c.constraint_type having count(cc.column_name) = 4;
Both these suggests assume a sensible naming convention and proper enforcement of relational integrity. Under the circumstances you might not be that lucky.
In which case, enable SQL Trace, run the report then peruse the trace file to see all the queries which provide the data. Your table will be one of those. Find out more.

How to create a view in a FOR loop in Oracle SQL

What I'm trying to do is create views based off a condition between two tables, and I want it to go through all tables that meet this condition.
I've been doing some research and I found that cursors would be helpful for this sort of thing, but I've been running into a "cursor out of scope" at line 15.
DECLARE
query_str VARCHAR2(32000);
CURSOR all_syn IS
SELECT SYNONYM_NAME, TABLE_NAME
FROM ALL_SYNONYMS
WHERE SYNONYM_NAME LIKE 'S!_AG!_%' ESCAPE '!';
CURSOR our_tables IS
SELECT TABLE_NAME
FROM ALL_TABLES
WHERE TABLE_NAME LIKE 'AG!_%1' ESCAPE '!';
BEGIN
query_str := 'CREATE OR REPLACE VIEW ' || LTRIM(all_syn.SYNONYM_NAME, 'S_') || 'AS
SELECT TO_CHAR(itemnum) itemnum,
TO_CHAR(keywordnum) keywordnum,
TO_CHAR(keysetnum) keysetnum,
MOD_BY_EMPLOYEE,
MOD_BY_PROCESS,
MOD_DATE_EMPLOYEE,
MOD_DATE_PROCESS
FROM all_syn.SYNONYM_NAME,
our_tables.TABLE_NAME
WHERE our_tables.TABLE_NAME = ' || LTRIM(all_syn.SYNONYM_NAME, 'S_');
FOR v_rec IN all_syn LOOP
IF (v_rec.TABLE_NAME LIKE 'KEYXITEM%') THEN
EXECUTE IMMEDIATE query_str;
END IF;
END LOOP;
END;
The reason I am doing this is because my company has tables that aren't directly connected to a certain 3rd party DB link, so they had me change the table names by putting a 1 at the end of the affected tables, creating synonyms for these tables with the DB link, and then make views of these synonyms with the original table name so that they now have the DB link and act as the original table so that we don't have to change any code. I have to join the synonym tables with the changed tables, because we added some attributes that the 3rd party tables don't have.
If anyone has any suggestions or advice, it would be greatly appreciated! I'm new to using dynamic sql and PL/SQL, so bear with me please.
EDIT:
So I've improved my code, and I feel like I'm getting closer to my desired results, however I'm getting this weird error:
line 28, column 52:
PLS-00357: Table,View Or Sequence reference 'ALL_TABLES.TABLE_NAME' not allowed in this context
Which doesn't make sense to me as I'm declaring it in the query.
BEGIN
FOR v_rec IN all_syn LOOP
IF (v_rec.TABLE_NAME LIKE 'KEYXITEM%') THEN
query_str := 'CREATE OR REPLACE VIEW ' || LTRIM(v_rec.SYNONYM_NAME, 'S_') || ' AS
SELECT itemnum AS item_num,
keywordnum AS key_word_num,
keysetnum AS key_set_num,
MOD_BY_EMPLOYEE,
MOD_BY_PROCESS,
MOD_DATE_EMPLOYEE,
MOD_DATE_PROCESS,
FROM ( SELECT TABLE_NAME
FROM ALL_TABLES
WHERE TABLE_NAME LIKE ' || '''AG!_%1''' || ' ESCAPE ' || '''!''' || '
AND ' || RTRIM(ALL_TABLES.TABLE_NAME, '1') ||' = ' || LTRIM(v_rec.SYNONYM_NAME, 'S_') || ') our_tables,
' || v_rec.SYNONYM_NAME;
-- EXECUTE IMMEDIATE query_str;
END IF;
dbms_output.put_line(query_str);
END LOOP;
END;
You cannot reference cursor like that. Move the query_str creation inside the FOR LOOP and reference the record variable.
EDIT: I've tried to fix the FROM/WHERE clause, but you might be missing a join condition there.
DECLARE
query_str VARCHAR2(32000);
CURSOR all_syn IS
SELECT SYNONYM_NAME, TABLE_NAME
FROM ALL_SYNONYMS
WHERE SYNONYM_NAME LIKE 'S!_AG!_%' ESCAPE '!';
CURSOR our_tables IS
SELECT TABLE_NAME
FROM ALL_TABLES
WHERE TABLE_NAME LIKE 'AG!_%1' ESCAPE '!';
BEGIN
FOR v_rec IN all_syn LOOP
IF (v_rec.TABLE_NAME LIKE 'KEYXITEM%') THEN
query_str := 'CREATE OR REPLACE VIEW ' || LTRIM(v_rec.SYNONYM_NAME, 'S_') || 'AS
SELECT TO_CHAR(itemnum) itemnum,
TO_CHAR(keywordnum) keywordnum,
TO_CHAR(keysetnum) keysetnum,
MOD_BY_EMPLOYEE,
MOD_BY_PROCESS,
MOD_DATE_EMPLOYEE,
MOD_DATE_PROCESS
FROM ' || v_rec.SYNONYM_NAME || ',
' || v_rec.TABLE_NAME || '
WHERE ' || v_rec.TABLE_NAME = ' || LTRIM(v_rec.SYNONYM_NAME, 'S_');
EXECUTE IMMEDIATE query_str;
END IF;
END LOOP;
END;

Alter every table of a schema that has a name like 'something'?

I'd like to know if it is possible to alter every single table in a schema that contains a column name like 'something' in Oracle DB.
You can use a loop to iterate over USER_TAB_COLUMNS and generate the SQL statement:
declare
l_SQL varchar2(4000);
begin
for cur in (
select table_name, column_name
from user_tab_columns utc
where upper(utc.column_name) like '%SOMETHING%')
loop
l_SQL := 'alter table ' || cur.table_name || ' drop column ' || cur.column_name;
dbms_output.put_line(l_SQL);
-- execute immediate l_SQL; -- UNCOMMENT TO RUN; DO NOT DO THIS IN PRODUCTION!
end loop;
end;
Yes this is possible. You have to dynamically create the DDL or DML and execute immediate out of a PL/SQL routine. With "alter" do you mean change the content of the tables columns or do you mean change the columns properties?
EDIT:
You can use Frank's Routine but for a column modify you do this.
l_SQL := 'alter table ' || cur.table_name ||
' modify (' || cur.column_name || ' varchar2(50)); ';
I agree with Frank to not blindly modify the columns, use the dbms output as a generated script.
EDIT2:
There is one more thing I realized. Table user_tab_columns gives you also columns of views. You could exclude them by joining with user_tables:
set serveroutput on
declare
l_SQL varchar2(4000);
begin
for cur in (
select utc.table_name, utc.column_name
from user_tab_columns utc
join user_tables ut on (UT.TABLE_NAME = utc.table_name)
where upper(utc.column_name) like '%SO')
loop
l_SQL := 'alter table ' || cur.table_name || ' modify (' || cur.column_name || ' varchar2(50)); ';
dbms_output.put_line(l_SQL);
-- execute immediate l_SQL; -- UNCOMMENT TO RUN; DO NOT DO THIS IN PRODUCTION!
end loop;
end;

create view and alter in all tables

I am attempting to create a logic within the procedure using cursors to create database views for all the tables and create a new column called HISTORY_DATE for all tables in the schema. I need help building the logic below.
create or replace PROCEDURE ALTER_TABLES(
RC OUT INT
,MSG OUT VARCHAR)
AS
BEGIN
DECLARE
CURSOR TBL_CUR IS
SELECT TABLE_NAME FROM USER_TABLES;
TBL_REC TBL_CUR%ROWTYPE;
SQL_STMT VARCHAR(2000);
BEGIN
OPEN TBL_CUR;
LOOP
FETCH TBL_CUR INTO TBL_REC;
EXIT WHEN TBL_CUR%NOTFOUND;
PRINT_DETAILS(TBL_REC.TABLE_NAME);
SQL_STMT:= 'ALTER TABLE '
|| TBL_REC.TABLE_NAME
|| ' ADD HISTORY_DATE DATE'
|| ' AND'
|| ' CREATE OR REPLACE VIEW all_tbl AS'
|| ' SELECT *'
|| ' FROM USER_TABLES'
;
PRINT_DETAILS(SQL_STMT);
EXECUTE IMMEDIATE SQL_STMT;
END LOOP;
CLOSE TBL_CUR;
rollback;
END;
END;
You only want to create the view once; and you cannot do two things at once as Gordon Linoff mentioned. So take the view creation outside of the loop; something like this (untested):
create or replace PROCEDURE ALTER_TABLES(RC OUT INT
,MSG OUT VARCHAR)
AS
BEGIN
DECLARE
CURSOR TBL_CUR IS
SELECT TABLE_NAME FROM USER_TABLES;
TBL_REC TBL_CUR%ROWTYPE;
SQL_STMT VARCHAR(2000);
BEGIN
SQL_STMT := 'CREATE OR REPLACE VIEW all_tbl AS'
|| ' SELECT *'
|| ' FROM USER_TABLES';
PRINT_DETAILS(SQL_STMT);
EXECUTE IMMEDIATE SQL_STMT;
OPEN TBL_CUR;
LOOP
FETCH TBL_CUR INTO TBL_REC;
EXIT WHEN TBL_CUR%NOTFOUND;
PRINT_DETAILS(TBL_REC.TABLE_NAME);
SQL_STMT:= 'ALTER TABLE '
|| TBL_REC.TABLE_NAME
|| ' ADD (HISTORY_DATE DATE)'
;
PRINT_DETAILS(SQL_STMT);
EXECUTE IMMEDIATE SQL_STMT;
END LOOP;
CLOSE TBL_CUR;
--rollback;
END;
Now, I don't see a need to create a view all_tbl as all it is, is a copy of the view USER_TABLES -- so just use USER_TABLES -- but I left it's creation there so if you need only certain columns from USER_TABLES or certain rows, you know where to place that.

SQL Select Like Column Name from table

The goal is to create a Oracle Function that is capable of query column name off a token provided by the user as to create a function with such capabilities
select cols_like('%e%') from table
This is the point I am currently at
CREATE OR REPLACE Function COLS_LIKE
(v_search in VARCHAR2, v_table in VARCHAR2)
RETURN VARCHAR
IS
TYPE r_cursor IS REF CURSOR;
c_emp r_cursor;
crs_cols VARCHAR(255);
column_list VARCHAR(1000);
BEGIN
OPEN c_emp FOR
'select COLUMN_NAME from cols
where TABLE_NAME = ''' || v_table || '''
and column_name like ''' || v_search || '''';
LOOP
FETCH c_emp INTO crs_cols;
EXIT WHEN c_emp%NOTFOUND;
if column_list IS NULL THEN
column_list := crs_cols;
else
column_list := column_list || ', ' || crs_cols;
end if;
END LOOP;
RETURN column_list;
END;
Where you call the function such as this
Declare
tests VARCHAR(100);
sql_stmt VARCHAR2(200);
begin
tests := COLS_LIKE('%E%', 'table');
DBMS_OUTPUT.PUT_LINE(tests);
-- OR
sql_stmt := 'select ' || COLS_LIKE('%E%', 'table') || ' from table';
DBMS_OUTPUT.PUT_LINE(sql_stmt);
end;
The end goal would be something such as this
select COLS_LIKE('%E%', 'table') from table;
What modifications can I make to my function or how I am calling to so that this function can be applied correctly.
Why you'd want to do such a thing I've no idea but you could return an open cursor to PL/SQL fairly easily:
create or replace function cols_like (
PTable in varchar2
, PColumn in varchar2
) return sys_refcursor
l_cols varchar2(32767);
c_curs sys_refcursor;
begin
select listagg(column_name, ', ') within group (order by column_id)
into l_cols
from user_tab_cols
where table_name = upper(Ptable)
and column_name like '%' || upper(PColumn) || '%'
;
open c_curs for '
select ' || l_cols || '
from ' || Ptable;
return c_curs;
end;
/
Returning this to a standard SQL statement will be a lot more difficult, this is because in selecting this function you're only selecting one column's worth of data. You want to be able to select N columns, which means you need to start returning nested tables that have been dynamically created.
I'm sure it's possible; but, before you get anywhere close to starting to attempt to do this think about why you're doing it. Ask a question where you don't state your end goal but where you state what your actual problem is. Chances are there's a lot simpler solution.
I was also having same problem and found this, and it is working for me. I was just making comparison of mobile numbers from two tables and in one of the tables some numbers have 0 in start and some don't. Finally got solution and just added % in start:
a.number was having numbers, in some of them starting 0 was missing. and b.number was accurate.
b.number like CONCAT('%',a.number)