I need to rename a constraint in an Oracle databse, but I don't know the old name at design-time.
What I would like to do is this:
declare
vOldName string;
begin
select CONSTRAINT_NAME
into vOldName
from user_constraints
where TABLE_NAME='AGREEMENT' and CONSTRAINT_TYPE='R';
alter table Agreement rename constraint vOldName to AGREEMENT_FK1;
end;
but I get the error message "PLS-00103: Encountered the symbol "ALTER" when expecting one of the following: begin case ".
How do I solve this problem?
Use dynamic PL/SQL:
declare
vOldName user_constraints.constraint_name%TYPE;
begin
select CONSTRAINT_NAME
into vOldName
from user_constraints
where TABLE_NAME='AGREEMENT' and CONSTRAINT_TYPE='R';
execute immediate 'alter table Agreement rename constraint '
|| vOldName || ' to AGREEMENT_FK1';
end;
Related
I have set of sql statements (create table, views, sequences), Where my schema name changes all the time and rest of the sql be the same.
Even in schema name a part of it has to change, for example:
I have a schema name ABC_XYZ, i would like to change this schema name to ABC_DEF_XYZ.
For this i tried to insert a variable in the schema name like ABC_&var1_XYZ. If i pass the variable in the schema name as shown here (ABC_&var1_XYZ) and pass the value to the variable, It ask me to declare the vaue of the variable.
I have given the error and the code i use below:
Error report -
ORA-06550: line 5, column 52:
PLS-00201: identifier 'REL4' must be declared
ORA-06550: line 5, column 1:
PL/SQL: Statement ignored
ORA-06550: line 7, column 51:
PLS-00201: identifier 'REL4' must be declared
here is my create statement:
set echo off
set verify off
undefine mySchemaPart
declare
vSQL varchar2(32767);
begin
vSQL:= 'begin EXECUTE IMMEDIATE alter table ABC_'||&&mySchemaPart||'_OWNER.TEST drop constraint EMPLOYEE_ID_FK; EXCEPTION when others then if (SQLCODE != -02443 and SQLCODE != -942) then RAISE; end if; end';
execute immediate vSQL;
vSQL:= 'begin EXECUTE IMMEDIATE drop table ABC_'||&mySchemaPart||'_OWNER.TEST cascade constraints PURGE; EXCEPTION when others then if SQLCODE != -942 then RAISE; end if; end';
execute immediate vSQL;
vSQL:= 'create table ABC_'||&mySchemaPart||'OWNER.TEST
(
EMPLOYEE_ID NUMBER(19) not null,
LAST_UPDT_DT DATE not null,
)';
execute immediate vSQL;
vSQL:= 'CREATE OR REPLACE SYNONYM ABC_USER.TEST FOR ABC_'||&mySchemaPart||'OWNER.TEST';
execute immediate vSQL;
vSQL:= 'begin EXECUTE IMMEDIATE alter table ABC_'||&mySchemaPart||'OWNER.TEST
add constraint EMPLOYEE_ID_FK foreign key (EMPLOYEE_ID)
references ABC_OWNER.GDSD (EMPLOYEE_ID); EXCEPTION when others then if (SQLCODE != -02443 and SQLCODE != -02275) then RAISE; end if; end';
execute immediate vSQL;
end;
/
Is there any other way i can try to insert my value in this schema.
You can use dynamic SQL to handle a varying schema in your DDL; for example:
accept mySchemaPart PROMPT "Schema part: "
declare
vMySchema varchar2(30) := 'ABC_' || '&mySchemaPart' || '_XYZ';
vSQL varchar2(32767);
begin
vSQL := 'create table ' || vMySchema || '.tableName (...)';
execute immediate vSQL;
end;
/
Pass it as 'ABC_||'&var1||'_XYZ'
An example of insert statement;
insert into test (a)
select 'ABC_'||&var1||'_XYZ' from dual ;
On execution it will ask for prompt.
As a block you can use this:
declare
var varchar2(100);
begin
var:='create table ABC_'||&var1||'_XYZ.tablnam(a number)'; --add your columns
dbms_output.put_line(var);
execute immediate var;
end;
As per #bin request his/her statments:
declare
var varchar2(500);
begin
var:='create table ABC_'||&var||'_OWNER.TEST(EMPLOYEE_ID NUMBER(19) not null, LAST_UPDT_DT DATE not null )';
--Table Created
dbms_output.put_line(var);
execute immediate var;
var:='alter table ABC_'||&var1||'_OWNER.TEST add constraint EMPLOYEE_ID_FK foreign key (EMPLOYEE_ID) references ABC_OWNER.GDSD(EMPLOYEE_ID)';
--Constraint Added
dbms_output.put_line(var);
execute immediate var;
var:='CREATE OR REPLACE SYNONYM ABC_USER.TEST FOR ABC_'||&var1||'_OWNER.TEST';
--Synonym Created
dbms_output.put_line(var);
execute immediate var;
var:='alter table ABC_'||&var1||'_OWNER.TEST drop constraint EMPLOYEE_ID_FK';
--Constraint Dropped
dbms_output.put_line(var);
execute immediate var;
var:='drop table ABC_'||&var1||'_OWNER.TEST cascade constraints PURGE';
--Table Dropped
dbms_output.put_line(var);
execute immediate var;
Exception
WHEN others then
dbms_output.put_line('Error--'||sqlerrm||'-'||sqlcode);
end;
Output:
SQL> declare
var varchar2(100);
begin
var:='create table ABC_'||&var1||'OWNER.TEST(EMPLOYEE_ID NUMBER(19) not null, LAST_UPDT_DT DATE not null )';
'
'
'
dbms_output.put_line(var);
execute immediate var;
end;
Enter value for var1: 'REL4'
create table ABC_REL4_OWNER.TEST(EMPLOYEE_ID NUMBER(19) not null, LAST_UPDT_DT DATE not null )
Table created.
alter table ABC_REL4_OWNER.TEST add constraint EMPLOYEE_ID_FK foreign key (EMPLOYEE_ID) references ABC_OWNER.GDSD(EMPLOYEE_ID)
Table altered.
CREATE OR REPLACE SYNONYM ABC_USER.TEST FOR ABC_REL4_OWNER.TEST
Synonym created.
alter table ABC_REL4_OWNER.TEST drop constraint EMPLOYEE_ID_FK
Table altered.
drop table ABC_REL4_OWNER.TEST cascade constraints PURGE
Table dropped.
I want to change a NOT NULL constraint of a clob column to a NULL constraint. However, when trying
ALTER TABLE myTable ALTER COLUMN myClobCol clob NULL;
or
ALTER TABLE myTable modify myClobCol clob NULL;
I get the following errors:
ORA-01735: invalid ALTER TABLE option
or
ORA-22859: invalid modification of columns
What am I doing wrong? Do I have to use a temp column in this case too? I know the scenario of changing the data type from clob to varchar2 by using a temp column but here I just want to change the constraint. Why this is not possible?
Thanks in advance!
You are trying to set the type of the column from CLOB to CLOB, it's invalid because any try to set an object column is invalid. Use just ALTER TABLE myTable modify myClobCol NULL; to set the NULL constraint of the column.
Try this:
declare
col_nullable varchar2(1);
begin
select nullable into col_nullable
from user_tab_columns
where table_name = 'myTable'
and column_name = 'myClobCol';
if col_nullable = 'N' then
execute immediate 'alter table mytable modify (myClobCol null)';
end if;
end;
I have checked the documentation provided by Oracle and found a way to modify a constraint without dropping the table. Problem is, it errors out at modify as it does not recognize the keyword.
Using EMS SQL Manager for PostgreSQL.
Alter table public.public_insurer_credit MODIFY CONSTRAINT public_insurer_credit_fk1
deferrable, initially deferred;
I was able to work around it by dropping the constraint using :
ALTER TABLE "public"."public_insurer_credit"
DROP CONSTRAINT "public_insurer_credit_fk1" RESTRICT;
ALTER TABLE "public"."public_insurer_credit"
ADD CONSTRAINT "public_insurer_credit_fk1" FOREIGN KEY ("branch_id", "order_id", "public_insurer_id")
REFERENCES "public"."order_public_insurer"("branch_id", "order_id", "public_insurer_id")
ON UPDATE CASCADE
ON DELETE NO ACTION
DEFERRABLE
INITIALLY DEFERRED;
There is no ALTER command for constraints in Postgres. The easiest way to accomplish this is to drop the constraint and re-add it with the desired parameters. Of course any change of the constraint will be run against the current table data.
BEGIN;
ALTER TABLE t1 DROP CONSTRAINT ...
ALTER TABLE t1 ADD CONSTRAINT ...
COMMIT;
According to the correct manual (which is supplied by PostgreSQL, not by Oracle), there is no modify constraint available in the ALTER TABLE statement:
Here is the link to the correct manual:
http://www.postgresql.org/docs/current/static/sql-altertable.html
As of version 9.4, PostgreSQL supports ALTER TABLE ... ALTER CONSTRAINT for foreign keys.
This features will "Allow constraint attributes to be altered,
so the default setting of NOT DEFERRABLE can be altered to DEFERRABLE and back." Looking at your question I think that is (kind of) what you have been looking for.
More detailed information and an example can be found here:
http://www.depesz.com/2013/06/30/waiting-for-9-4-alter-table-alter-constraint-for-fks/
ALTER CONSTRAINT would require knowing of foreign key name, which is not always convenient.
Here is function, where you need to know only table and column names.
Usage:
select replace_foreign_key('user_rates_posts', 'post_id', 'ON DELETE CASCADE');
Function:
CREATE OR REPLACE FUNCTION
replace_foreign_key(f_table VARCHAR, f_column VARCHAR, new_options VARCHAR)
RETURNS VARCHAR
AS $$
DECLARE constraint_name varchar;
DECLARE reftable varchar;
DECLARE refcolumn varchar;
BEGIN
SELECT tc.constraint_name, ccu.table_name AS foreign_table_name, ccu.column_name AS foreign_column_name
FROM
information_schema.table_constraints AS tc
JOIN information_schema.key_column_usage AS kcu
ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage AS ccu
ON ccu.constraint_name = tc.constraint_name
WHERE constraint_type = 'FOREIGN KEY'
AND tc.table_name= f_table AND kcu.column_name= f_column
INTO constraint_name, reftable, refcolumn;
EXECUTE 'alter table ' || f_table || ' drop constraint ' || constraint_name ||
', ADD CONSTRAINT ' || constraint_name || ' FOREIGN KEY (' || f_column || ') ' ||
' REFERENCES ' || reftable || '(' || refcolumn || ') ' || new_options || ';';
RETURN 'Constraint replaced: ' || constraint_name || ' (' || f_table || '.' || f_column ||
' -> ' || reftable || '.' || refcolumn || '); New options: ' || new_options;
END;
$$ LANGUAGE plpgsql;
Be aware: this function won't copy attributes of initial foreign key.
It only takes foreign table name / column name, drops current key and replaces with new one.
Does Postgres have any way to say ALTER TABLE foo ADD CONSTRAINT bar ... which will just ignore the command if the constraint already exists, so that it doesn't raise an error?
A possible solution is to simply use DROP IF EXISTS before creating the new constraint.
ALTER TABLE foo DROP CONSTRAINT IF EXISTS bar;
ALTER TABLE foo ADD CONSTRAINT bar ...;
Seems easier than trying to query information_schema or catalogs, but might be slow on huge tables since it always recreates the constraint.
Edit 2015-07-13:
Kev pointed out in his answer that my solution creates a short window when the constraint doesn't exist and is not being enforced. While this is true, you can avoid such a window quite easily by wrapping both statements in a transaction.
This might help, although it may be a bit of a dirty hack:
create or replace function create_constraint_if_not_exists (
t_name text, c_name text, constraint_sql text
)
returns void AS
$$
begin
-- Look for our constraint
if not exists (select constraint_name
from information_schema.constraint_column_usage
where table_name = t_name and constraint_name = c_name) then
execute constraint_sql;
end if;
end;
$$ language 'plpgsql'
Then call with:
SELECT create_constraint_if_not_exists(
'foo',
'bar',
'ALTER TABLE foo ADD CONSTRAINT bar CHECK (foobies < 100);')
Updated:
As per Webmut's answer below suggesting:
ALTER TABLE foo DROP CONSTRAINT IF EXISTS bar;
ALTER TABLE foo ADD CONSTRAINT bar ...;
That's probably fine in your development database, or where you know you can shut out the apps that depend on this database for a maintenance window.
But if this is a lively mission critical 24x7 production environment you don't really want to be dropping constraints willy nilly like this. Even for a few milliseconds there's a short window where you're no longer enforcing your constraint which may allow errant values to slip through. That may have unintended consequences leading to considerable business costs at some point down the road.
You can use an exception handler inside an anonymous DO block to catch the duplicate object error.
DO $$
BEGIN
BEGIN
ALTER TABLE foo ADD CONSTRAINT bar ... ;
EXCEPTION
WHEN duplicate_table THEN -- postgres raises duplicate_table at surprising times. Ex.: for UNIQUE constraints.
WHEN duplicate_object THEN
RAISE NOTICE 'Table constraint foo.bar already exists';
END;
END $$;
http://www.postgresql.org/docs/9.4/static/sql-do.html http://www.postgresql.org/docs/9.4/static/plpgsql-control-structures.html
http://www.postgresql.org/docs/9.4/static/errcodes-appendix.html
you can run query over pg_constraint table to find constraint exists or not.like:
SELECT 1 FROM pg_constraint WHERE conname = 'constraint_name'"
Creating constraints can be an expensive operation on a table containing lots of data so I recommend not dropping constraints only to immediately create them again immediately after - you only want to create that thing once.
I chose to solve this using an anonymous code block, very similar to Mike Stankavich, however unlike Mike (who catches an error) I first check to see if the constraint exists:
DO $$
BEGIN
IF NOT EXISTS ( SELECT constraint_schema
, constraint_name
FROM information_schema.check_constraints
WHERE constraint_schema = 'myschema'
AND constraint_name = 'myconstraintname'
)
THEN
ALTER TABLE myschema.mytable ADD CONSTRAINT myconstraintname CHECK (column <= 100);
END IF;
END$$;
Using information_schema.constraint_column_usage to check for the constraint doesn't work for foreign keys. I use pg_constraint to check for primary keys, foreign keys or unique constraints:
CREATE OR REPLACE FUNCTION add_constraint(t_name text, c_name text, constraint_sql text)
RETURNS void
AS $$
BEGIN
IF NOT EXISTS(
SELECT c.conname
FROM pg_constraint AS c
INNER JOIN pg_class AS t ON c.conrelid = t."oid"
WHERE t.relname = t_name AND c.conname = c_name
) THEN
EXECUTE 'ALTER TABLE ' || t_name || ' ADD CONSTRAINT ' || c_name || ' ' || constraint_sql;
END IF;
END;
$$
LANGUAGE plpgsql;
Examples:
SELECT add_constraint('client_grant_system_scopes', 'client_grant_system_scopes_pk', 'PRIMARY KEY (client_grants_id, tenant, "scope");');
SELECT add_constraint('client_grant_system_scopes', 'client_grant_system_scopes_fk', 'FOREIGN KEY (tenant,"scope") REFERENCES system_scope(tenant,"scope") ON DELETE CASCADE;');
SELECT add_constraint('jwt_assertion_issuers', 'jwt_assertion_issuers_issuer_key', 'UNIQUE (issuer);');
Take advantage of regclass to reduce verbosity, increase performance, and avoid errors related to table naming clashes between schemas:
DO $$ BEGIN
IF NOT EXISTS (SELECT FROM pg_constraint
WHERE conrelid = 'foo'::regclass AND conname = 'bar') THEN
ALTER TABLE foo ADD CONSTRAINT bar...;
END IF;
END $$;
This will also work for tables in other schemas, e.g.:
DO $$ BEGIN
IF NOT EXISTS (SELECT FROM pg_constraint
WHERE conrelid = 's.foo'::regclass AND conname = 'bar') THEN
ALTER TABLE s.foo ADD CONSTRAINT bar...;
END IF;
END $$;
In psql You can use metacommand \gexec for run generated query.
SELECT 'ALTER TABLE xx ADD CONSTRAINT abc' WHERE not EXISTS (SELECT True FROM pg_constraint WHERE conname = 'abc') \gexec
For me those solutions didn't work because the constraint was a primary key.
This one worked for me:
ALTER TABLE <table.name> DROP CONSTRAINT IF EXISTS <constraint.name> CASCADE;
Considering all the above mentioned answers , the below approach help if you just want to check if a constraint exist in the table in which you are trying to insert and raise a notice if there happens to be one
DO
$$ BEGIN
IF NOT EXISTS (select constraint_name
from information_schema.table_constraints
where table_schema='schame_name' and upper(table_name) =
upper('table_name') and upper(constraint_name) = upper('constraint_name'))
THEN
ALTER TABLE TABLE_NAME ADD CONSTRAINT CONTRAINT_NAME..... ;
ELSE raise NOTICE 'Constraint CONTRAINT_NAME already exists in Table TABLE_NAME';
END IF;
END
$$;
Don't know why so many lines of code ?
-- SELECT "Column1", "Column2", "Column3" , count(star) FROM dbo."MyTable" GROUP BY "Column1" , "Column2" , "Column3" HAVING count(*) > 1;
alter table dbo."MyTable" drop constraint if exists "MyConstraint_Name" ;
ALTER TABLE dbo."MyTable" ADD CONSTRAINT "MyConstraint_Name" UNIQUE("Column1", "Column3", "Column2");
In Oracle 10g, how can I drop a unique constraint on a column without knowing the name of the constraint (e.g. a system generated name, which won't necessarily be the same across database instances)? Dropping and recreating the table isn't an option. Is it possible?
You can retrieve the constraint's name with:
SELECT CONSTRAINT_NAME
FROM USER_CONSTRAINTS WHERE TABLE_NAME = 'xxx'
AND CONSTRAINT_TYPE = 'U'
You can for instance create a stored procedure that executes the previous sql, stores its result in a variable and uses this variable in ALTER TABLE DROP CONSTRAINT
EDIT: e.g.:
BEGIN
FOR r IN (
SELECT TABLE_NAME, CONSTRAINT_NAME
FROM USER_CONSTRAINTS WHERE TABLE_NAME = 'xxx'
AND CONSTRAINT_TYPE = 'U') LOOP
EXECUTE IMMEDIATE REPLACE(REPLACE(
'ALTER TABLE #TABLE# DROP CONSTRAINT #CON#'
,'#TABLE#',r.TABLE_NAME)
,'#CON#',r.CONSTRAINT_NAME);
END LOOP;
END;