Using Oracle 10g, I need to rename a bunch of FK constraints which all end in LITE to include an FK prefix.
My thinking was (I've ensured all names are short enough to accommodate the prefix):
DECLARE
v_name VARCHAR2(30 BYTE);
v_new_name VARCHAR2(30 BYTE);
CURSOR c1 is select CONSTRAINT name from user_constraints where constraint_type = 'R' and constraint_name like '%_LITE';
BEGIN
OPEN c1;
LOOP
FETCH c1 into v_name;
EXIT when c1%NOTFOUND;
v_new_name:= 'FK_' || v_name;
update user_constraints SET constraint_name = v_new_name where constraint_name = v_name;
END LOOP;
close c1;
END;
Any reason why that would be unsafe and I should have to create alter table statements instead?
USER_CONSTRAINTS is a view, you cannot update it as a normal user. EDIT: Even SYS cannot do that, and doing updates on the data dictionary seems like an incredibly bad idea to me.
Better use ALTER TABLE xxx RENAME CONSTRAINT yyy TO zzz;
As ammoQ says, don't even think about doing that! This is the equivalent code using ALTER TABLE:
BEGIN
FOR r IN (select constraint_name
from user_constraints
where constraint_type = 'R'
and constraint_name like '%_LITE'
)
LOOP
EXECUTE IMMEDIATE 'ALTER TABLE ' || r.table_name
|| ' RENAME CONSTRAINT ' || r.constraint_name
|| ' TO FK_' ||v_new_name;
END LOOP;
END;
Related
I am new to Oracle, I am trying to write a procedure in Oracle to delete foreign key constraints for a table. I have already done this for MySQL and its working. I am not sure with the syntax, apologies for that, but my query is working If I run it individually. I want to do the same thing(removing foreign key constraints) for multiple tables and don't want to write queries multiple times. hence first I am finding the foreign keys associated with that table , storing them in cursor and later removing all of the foreign keys by creating and executing drop constraints query associated with that table. following code is giving multiple errors to me.
CREATE OR REPLACE PROCEDURE removeConstraintsForTable(vTableName IN varchar2) IS
BEGIN
cName VARCHAR(2048);
sql_stmt VARCHAR2(2048);
CURSOR cur IS
SELECT DISTINCT CONSTRAINT_NAME
FROM ALL_CONSTRAINTS WHERE OWNER= sys_context('userenv','current_schema')
AND TABLE_NAME = vTableName AND CONSTRAINT_TYPE='R';
BEGIN
OPEN cur;
LOOP
FETCH cur INTO cName;
EXIT WHEN cur%notfound;
sql_stmt := CONCAT(CONCAT(CONCAT('ALTER TABLE ',vTableName),CONCAT(' DROP FOREIGN KEY ',cName)),';');
SELECT sql_stmt FROM dual;
INSERT INTO TEMP(Name) VALUES(sql_stmt);
COMMIT;
END LOOP;
END
/
CALL removeConstraintsForTable('table1');
CALL removeConstraintsForTable('table2');
CALL removeConstraintsForTable('table3');
CALL removeConstraintsForTable('table4');
COMMIT;
You have an extra BEGIN right at the start of your procedure, and the final END is missing a semicolon. You shouldn't really be using VARCHAR, and you could declare the cName variable using the data dictionary anyway; however an implicit loop will be simpler, as will using the concatenation operator || instead of nested CONCAT() calls, and the generated statement should not end in a semicolon:
create or replace procedure removeconstraintsfortable(p_table_name in varchar2) is
sql_stmt varchar2(2048);
begin
for rec in (
select owner, constraint_name
from all_constraints
where owner = sys_context('userenv','current_schema')
and table_name = p_table_name
and constraint_type = 'R'
)
loop
sql_stmt := 'ALTER TABLE "' || rec.owner || '"."' || p_table_name || '"'
|| ' DROP CONSTRAINT "' || rec.constraint_name || '"';
insert into temp(name) values(sql_stmt);
end loop;
commit;
end;
/
As pointed out in comments, the generated statement should be drop constraint.
I'm not sure why you're inserting into a table or where you execute the statement, but you can do it all in one if you prefer:
create or replace procedure removeconstraintsfortable(p_table_name in varchar2) is
sql_stmt varchar2(2048);
begin
for rec in (
select owner, constraint_name
from all_constraints
where owner = sys_context('userenv','current_schema')
and table_name = p_table_name
and constraint_type = 'R'
)
loop
sql_stmt := 'ALTER TABLE "' || rec.owner || '"."' || p_table_name || '"'
|| ' DROP CONSTRAINT "' || rec.constraint_name || '"';
dbms_output.put_line(sql_stmt);
execute immediate sql_stmt;
end loop;
end;
/
The dbms_output call just shows you the generated statement(s), before execute immediate executes it, well, immediately.
Quick demo; very basic table set-up:
create table t42 (id number primary key);
create table t43 (id number references t42 (id));
select table_name, constraint_name, constraint_type
from all_constraints
where table_name in ('T42', 'T43');
TABLE_NAME CONSTRAINT_NAME C
------------------------------ ------------------------------ -
T43 SYS_C00138153 R
T42 SYS_C00138152 P
Then call the procedure which shows the generated statement:
set serveroutput on
exec removeConstraintsForTable('T43');
ALTER TABLE "STACKOVERFLOW"."T43" DROP CONSTRAINT "SYS_C00138153"
PL/SQL procedure successfully completed.
and then check the constraint has gone:
select table_name, constraint_name, constraint_type
from all_constraints
where table_name in ('T42', 'T43');
TABLE_NAME CONSTRAINT_NAME C
------------------------------ ------------------------------ -
T42 SYS_C00138152 P
I fixed a number of syntax issues for you. Try this.
CREATE OR REPLACE PROCEDURE removeConstraintsForTable(vTableName IN varchar2) IS
cName VARCHAR2(30); -- identifiers are max 30 chars
sql_stmt VARCHAR2(2048);
CURSOR cur IS
SELECT DISTINCT CONSTRAINT_NAME
FROM USER_CONSTRAINTS
WHERE TABLE_NAME = vTableName AND CONSTRAINT_TYPE='R';
BEGIN
OPEN cur;
LOOP
FETCH cur INTO cName;
EXIT WHEN cur%notfound;
sql_stmt := 'ALTER TABLE ' || vTableName || ' DROP CONSTRAINT ' || cName;
INSERT INTO RANGERADMIN1.TEMP(Name) VALUES(sql_stmt);
END LOOP;
COMMIT;
END removeConstraintsForTable;
/
call removeConstraintsForTable('table1');
call removeConstraintsForTable('table2');
call removeConstraintsForTable('table3');
call removeConstraintsForTable('table4');
-- COMMIT; -- not necessary
I would like to rename a constraint, in oracle database, without specify it's name, but with selecting it's name. I would like to do something like this:
ALTER TABLE O4Y_USER RENAME CONSTRAINT
(SELECT constraint_name
FROM user_constraints
WHERE table_name = 'O4Y_USER'
AND constraint_type = 'P'
) TO 'O4Y_USER_PK';
It is not working, I have the following error
The select alone, is working well, it returns the correct value. How is the correct syntax to do, to work the alter statement?
You can use dynamic SQL. Something like:
DECLARE
p_constraint_name VARCHAR2(30);
p_sql VARCHAR2(4000);
BEGIN
SELECT constraint_name
INTO p_constraint_name
FROM user_constraints
WHERE table_name = 'O4Y_USER'
AND constraint_type = 'P';
p_sql := 'ALTER TABLE O4Y_USER RENAME CONSTRAINT "'
|| p_constraint_name
|| '" TO ''O4Y_USER_PK''';
DBMS_OUTPUT.PUT_LINE( p_sql );
EXECUTE IMMEDIATE p_sql;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE( 'Constraint not found!' );
END;
/
And just the same method using SQL*Plus or similar command line tools.
SQL> create table t ( x int primary key );
Table created.
SQL>
SQL> col cname new_value x
SQL>
SQL> SELECT constraint_name cname
2 FROM user_constraints
3 WHERE table_name = 'T'
4 AND constraint_type = 'P';
CNAME
----------------------------------------------------------------------
SYS_C0068724
SQL>
SQL> ALTER TABLE t RENAME CONSTRAINT &&x to my_new_name;
old 1: ALTER TABLE t RENAME CONSTRAINT &&x to my_new_name
new 1: ALTER TABLE t RENAME CONSTRAINT SYS_C0068724 to my_new_name
Table altered.
I get following error when I am attempting to disable constraints from hr.employees table
Error:
Error report -
ORA-02250: missing or invalid constraint name
ORA-06512: at line 14
02250. 00000 - "missing or invalid constraint name"
*Cause: The constraint name is missing or invalid.
*Action: Specify a valid identifier name for the constraint name.
Following is the code
DECLARE
CURSOR C1 IS SELECT CONSTRAINT_NAME FROM USER_CONSTRAINTS WHERE TABLE_NAME ='EMPLOYEES';
v_con_name VARCHAR2(20);
SQL_STATMENT VARCHAR2(100);
BEGIN
SQL_STATMENT := 'ALTER TABLE HR.EMPLOYEES DISABLE CONSTRAINT :A';
OPEN C1;
LOOP
FETCH C1 INTO v_con_name;
EXIT WHEN C1%NOTFOUND;
EXECUTE IMMEDIATE SQL_STATMENT USING v_con_name;
DBMS_OUTPUT.PUT_LINE(v_con_name);
END LOOP;
CLOSE C1;
END;
/
When I comment following line
EXECUTE IMMEDIATE SQL_STATMENT USING v_con_name;
Scripts execute successfully and provide following results
EMP_LAST_NAME_NN
EMP_EMAIL_NN
EMP_HIRE_DATE_NN
EMP_JOB_NN
EMP_SALARY_MIN
EMP_EMAIL_UK
EMP_EMP_ID_PK
EMP_DEPT_FK
EMP_JOB_FK
EMP_MANAGER_FK
PL/SQL procedure successfully completed.
Hence I understand that the cursor construct is fetching the desired constraints name and also outside of this plsql block, I could successfully alter employees table by disabling these constraints.
Please note that I have logged in as HR schema in Oracle 11g R2 XE database
I am not sure why I am getting the missing or invalid constraint name.. thanks to help me here.
You can't use bind variables for the names of database objects (or for DDL in general). You will need to build the entire ALTER statement by concatenating the name. Try something like this:
declare
k_tablename constant user_constraints.table_name%type := 'EMPLOYEES';
begin
for r in (
select constraint_name
, 'alter table ' || c.table_name || ' disable constraint ' || c.constraint_name as sql_statement
from user_constraints c
where table_name = k_tablename
)
loop
execute immediate r.sql_statement;
dbms_output.put_line('Disabled constraint ' || k_tablename || '.' || r.constraint_name);
end loop;
end;
/
Why don't you use this script to generate the DISABLE script?
SELECT 'ALTER TABLE '||OWNER||'.'||TABLE_NAME||' DISABLE CONSTRAINT '||CONSTRAINT_NAME||';' STMNT
FROM DBA_CONSTRAINTS
WHERE
R_CONSTRAINT_NAME IN
(SELECT CONSTRAINT_NAME
FROM DBA_CONSTRAINTS
WHERE CONSTRAINT_TYPE IN ('P','U')
AND OWNER = 'HR'
AND TABLE_NAME IN
(
'EMPLOYEES'
));
Somewhat simpler (& working) then the original attempt:
SQL> create table test (id number constraint pk_test primary key,
2 ime varchar2(20) constraint ch_ime check (ime in ('little', 'foot')));
Table created.
SQL>
SQL> begin
2 for c1 in (select table_name, constraint_name
3 from user_constraints
4 where table_name = 'TEST')
5 loop
6 execute immediate 'alter table ' || c1.table_name ||
7 ' disable constraint ' || c1.constraint_name;
8 end loop;
9 end;
10 /
PL/SQL procedure successfully completed.
SQL> select constraint_name, status From user_constraints where table_name = 'TEST';
CONSTRAINT_NAME STATUS
------------------------------ --------
CH_IME DISABLED
PK_TEST DISABLED
SQL>
I'm trying to drop a schema in oracle 11g on our dev environment and I get back SQL Error: No more data to read from socket. There is no load on the schema as it's just a dev db. It's a small db without anything crazy going on. I see this error all the time. Restarting the instance sometimes resolves the problem. I can't seem to find any information that would point to a solution. Thanks!
I understand that this message often arises due to a bug. Also, when it appears an entry in your alert log and/or a trace file will contain more detail on what the error might actually be. To find your trace file for the session run:
select U_DUMP.value
|| '/'
|| DB_NAME.value
|| '_ora_'
|| V$PROCESS.SPID
|| nvl2(V$PROCESS.TRACEID, '_' || V$PROCESS.TRACEID, null)
|| '.trc'
"Trace File"
from V$PARAMETER U_DUMP
cross join V$PARAMETER DB_NAME
cross join V$PROCESS
join V$SESSION
on V$PROCESS.ADDR = V$SESSION.PADDR
where U_DUMP.NAME = 'user_dump_dest'
and DB_NAME.NAME = 'db_name'
and v$session.audsid=sys_context('userenv','sessionid');
A dba at my company gave me this one. It's
CREATE OR REPLACE PROCEDURE "SYS"."DROP_SCHEMA_FAST" (pSchema IN
VARCHAR2)
IS
cnt NUMBER(5) := 0;
sql1 varchar2(4000);
x PLS_INTEGER;
--disable constraints:
cursor cur1 is select 'alter table ' || OWNER ||'.'||table_name||' disable constraint '||constraint_name sql2
from all_constraints where owner=pSchema and status='ENABLED'
and table_name not like 'BIN$%' and constraint_name not like 'SYS_%' and constraint_name not like '%PK%';
cursor cur2 is select 'alter table ' || OWNER ||'.'||table_name||' disable constraint '||constraint_name sql2
from all_constraints where owner=pSchema and status='ENABLED'
and table_name not like 'BIN$%' and constraint_name not like 'SYS_%';
--truncate all tables:
cursor cur3 is select 'truncate table ' || OWNER ||'.'||table_name sql2 from all_tables where owner=pSchema
and table_name not like 'BIN$%';
BEGIN
SELECT COUNT(*) INTO cnt FROM dba_users WHERE UPPER(username) = UPPER(pSchema);
IF (cnt <= 0) THEN
RETURN;
END IF;
sql1 := 'ALTER USER ' || UPPER(pSchema) || ' ACCOUNT LOCK';
EXECUTE IMMEDIATE sql1;
--disable constraints:
FOR ao_rec IN cur1 LOOP
EXECUTE IMMEDIATE ao_rec.sql2;
END LOOP;
FOR ao_rec IN cur2 LOOP
EXECUTE IMMEDIATE ao_rec.sql2;
END LOOP;
--truncate all tables:
FOR ao_rec IN cur3 LOOP
EXECUTE IMMEDIATE ao_rec.sql2;
END LOOP;
--drop schema:
sql1 := 'DROP USER ' || UPPER(pSchema) || ' CASCADE';
EXECUTE IMMEDIATE sql1;
exception when others then null;
END;
Also had this problem, got fixed by setting "PLScope identifiers:" to "None" in Tools->Preferences ->Database->PL/SQL Compiler
Does anybody know how to run all the lines generated from the following query as scripts on their own right?
select 'DROP TABLE '||table_name||' CASCADE CONSTRAINTS;' from user_tables;
What I'm basically trying to do, is delete all the user tables and constraints on my DB (this is oracle). The output I get is correct, but I want to know how I would run all the lines without copy/pasting.
Also, is there a more efficient way to drop all tables (including constraints)?
begin
for i in (select table_name from user_tables)
loop
execute immediate ('drop table ' || i.table_name || ' cascade constraints');
end loop;
end;
/
Justin Cave brought up an excellent point - the following will drop tables within the user's schema starting at the outermost branches of the hierarchy of dependencies, assuming all foreign keys reference the primary key, not a unique constraint. Tables without primary keys would be dropped last.
begin
for i in (select parent_table, max(tree_depth) as tree_depth
from (select parent.table_name as parent_table,
child.constraint_name as foreign_key,
child.table_name as child_table,
LEVEL AS TREE_DEPTH
from (select table_name, constraint_name
from USER_constraints
where constraint_type = 'P'
) parent
LEFT JOIN
(SELECT TABLE_NAME, CONSTRAINT_NAME,
r_constraint_name
FROM USER_CONSTRAINTS
WHERE CONSTRAINT_TYPE = 'R') child
on parent.constraint_name =
child.r_constraint_name
CONNECT BY NOCYCLE
(PRIOR CHILD.TABLE_NAME = PARENT.TABLE_NAME)
UNION
select DT.table_name as parent_table,
NULL AS FOREIGN_KEY, NULL AS CHILD_TABLE,
0 AS TREE_DEPTH
FROM USER_TABLES DT
WHERE TABLE_NAME NOT IN
(SELECT TABLE_NAME
FROM USER_CONSTRAINTS
WHERE CONSTRAINT_TYPE = 'P')
)
group by parent_table
order by 2 desc
)
loop
execute immediate ('drop table ' || i.parent_table ||
' cascade constraints');
end loop;
end;
/
The quick and dirty solution would be to do something like
FOR x IN (SELECT * FROM user_tables)
LOOP
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE ' || x.table_name ||
' CASCADE CONSTRAINTS';
EXCEPTION
WHEN others THEN
dbms_output.put_line( 'Failed to drop ' || x.table_name );
END;
END LOOP;
and run that a number of times until all the tables had been dropped. This will take multiple passes because you can't drop a parent table while there are still child tables with foreign keys that reference the parent.
The cleaner option would be to write a hierarchal query against the data dictionary to get the child tables, the parents of those children, the grandparents, etc. and to walk the tree to drop the appropriate objects. That should avoid errors but it would require a bit more work to code.
execute immediate - pass in the generated string
It is generally more efficient when dropping tables to use the truncate statement.
You can execute dynamic scripts using the execute immediate command