Within the Oracle documentation, the (DBA|ALL|USER)_CONSTRAINTS view has the following columns (among others)
OWNER Owner of the constraint definition
CONSTRAINT_NAME Name of the constraint definition
TABLE_NAME Name associated with the table (or view) with constraint definition
My question is which of these columns uniquely identifies a constraint? Is it just OWNER and CONSTRAINT_NAME indicating that the constraint name must be unique within the scope of that OWNER schema, or is it OWNER, TABLE_NAME, and CONSTRAINT_NAME indicating that the constraint name must only be unique within the scope of that OWNER.TABLE_NAME?
According to documentation: https://docs.oracle.com/database/121/SQLRF/sql_elements008.htm#SQLRF00223
Each of the following schema objects has its own namespace:
Constraints
Which suggests, that uniquely identifiyng the constraint is it's name and the schema/owner of the constraint.
One point here - it depends what you consider 'unique'.
Let's consider you have a constraint. You will drop the constraint and recreate it with different definition. The constraint has still the same name, but it has a different OBJECT_ID from the database point of view - it is a different object.
It is still unique from the point of current state of the database, but not for the lifetime of it. So, if you want to make sure that you're still working with the same constraint (not only constrain with the same name as before), you might want to query OBJECT_ID.DBA/ALL/USER_OBJECTS instead.
Related
I want to know if there is some specific way to obtain a table-level check constraint in Oracle and in PostgreSQL.
I can obtain all the check constraints in a table, but I want to obtain only this specific check constraint, I don't know if there is any specific query.
Thanks!
In PostgreSQL there is a System Catalog pg_constraint.
The catalog pg_constraint stores check, primary key, unique, foreign
key, and exclusion constraints on tables. (Column constraints are not
treated specially. Every column constraint is equivalent to some table
constraint.) Not-null constraints are represented in the pg_attribute
catalog, not here.
User-defined constraint triggers (created with CREATE CONSTRAINT
TRIGGER) also give rise to an entry in this table.
Check constraints on domains are stored here, too.
SELECT
*
FROM
pg_constraint
WHERE
contype = 'c' AND -- check constraint
conrelid != 0 AND -- table constraint
conname = 'my_check';
The contype column contains the constraint type, c is for check constraint.
The conrelid column contains the oid of the table this constraint is on, 0 if not a table constraint.
For Oracle basic view is ALL_CONSTRAINTS
Query, to obtain specific constraint in specific table:
SELECT *
FROM all_constraints
WHERE constraint_name LIKE upper('%&your_costraint%')
AND table_name LIKE upper('%&your_table%');
In Oracle, if you try to explicitly define the schema for a constraint in a CREATE TABLE statement it will result in a ORA-00904: : invalid identifier error:
CREATE TABLE SCHEMA1.MY_TABLE
(
TABLE_ID NUMBER(5)
, FLAG VARCHAR2(1) DEFAULT 'F'
, CONSTRAINT SCHEMA1.MY_TABLE_PK PRIMARY KEY (TABLE_ID) -- Parser doesn't like 'SCHEMA1'
, CONSTRAINT SCHEMA1.MY_TABLE_FLAG_CK CHECK (FLAG IN ('T', 'F')) --Same issue
);
This is no big deal because the constraint OWNER defaults to the same schema as defined in the CREATE TABLE declaration (or at least in the schema you are signed into - my accounts don't have the rights to validate). This can be confirmed by pulling up the constraint in ALL_CONSTRAINTS or DBA_CONSTRAINTS and viewing the OWNER value (which would read SCHEMA1 for the two constraints above).
But if you then follow up this statement with a second duplicate statement under a different schema:
CREATE TABLE SCHEMA2.MY_TABLE
(
TABLE_ID NUMBER(5)
, FLAG VARCHAR2(1) DEFAULT 'F'
, CONSTRAINT MY_TABLE_PK PRIMARY KEY (TABLE_ID) --This constraint already exists
, CONSTRAINT MY_TABLE_FLAG_CK CHECK (FLAG IN ('T', 'F')) --This one too
);
this results in an ORA-00955: name is already used by an existing object error.
My understanding of constraints are that they are just another object on the database which I would have assumed were subject to the same ownership rules with which I'm familiar. But based on the behavior above it is clear that they do not behave like most objects on the database.
Questions
What is it about constraints that requires their names be unique across all schemas (unlike other objects on the database)?
Does anyone know the technical reason for this naming requirement?
For context, I've run into a scenario at work where I had wanted to store duplicate names under different schemas (don't ask... it was inherited duplication and I'm just trying to stay consistent until I have funding to refactor). Now, I realize that I can very simply add the schema to the name and bypass this problem quickly but this rubs my OCD the wrong way so I'd like to better understand just why I can't do what I'm trying to do.
----------------------------- UPDATE ---------------------------
Okay... so I'm a fool. Please note that the above scenario cannot be repeated given a clean environment. After trying to reproduce the issue, I now realize what happened.
I'm currently working on cleaning up some DDL statements in preparation for a production release. I'm not the only developer in this environment and my offshore team has been working on these same tables. I have been editing some preexisting DDL scripts in preparation for a production release and had added some needed constraints to a few CREATE TABLE statements.
It would appear that I had failed to run my DROP scripts before running the second statement in SCHEMA2. My confusion was due to thinking that I was getting the ORA-00955 error because of the new constraints I was adding when in actuality it was because the SCHEMA2.MY_TABLE already existed in this environment. This misdiagnosis was compounded by me changing my constraint names and then rerunning all of my scripts successfully (as I must have run my DROP statements in all schemas before retesting). As a result I thought that I had observed unforeseen behavior when I really had not.
Thank you to everyone who commented for showing me the light!
What's missing in your question is the user under which you run the script. But here's a little experiment.
Connected to the database as user system. I have schemas NGM42 and NGM41 available for the experiment.
CREATE TABLE NGM41.MY_TABLE
(
TABLE_ID NUMBER(5)
, FLAG VARCHAR2(1) DEFAULT 'F'
, CONSTRAINT MY_TABLE_PK PRIMARY KEY (TABLE_ID) -- Parser doesn't like 'SCHEMA1'
, CONSTRAINT MY_TABLE_FLAG_CK CHECK (FLAG IN ('T', 'F')) --Same issue
);
CREATE TABLE NGM42.MY_TABLE
(
TABLE_ID NUMBER(5)
, FLAG VARCHAR2(1) DEFAULT 'F'
, CONSTRAINT MY_TABLE_PK PRIMARY KEY (TABLE_ID)
, CONSTRAINT MY_TABLE_FLAG_CK CHECK (FLAG IN ('T', 'F'))
);
Both statements run succesfully. So what happened to the constraints?
select owner,constraint_name from all_constraints
where constraint_name in ('MY_TABLE_PK','MY_TABLE_FLAG_CK')
NGM42 MY_TABLE_PK
NGM41 MY_TABLE_PK
NGM41 MY_TABLE_FLAG_CK
NGM42 MY_TABLE_FLAG_CK
The constraints are created in the same schema as the table. As you can see they do not need to be globally unique in the database.
I need to modify an existing PK. Therefore I drop an recreate it.
ALTER TABLE B DROP CONSTRAINT PK_B;
ALTER TABLE B ADD CONSTRAINT PK_B PRIMARY KEY ("TYP", "NR", "HH", "QUART");
Unfortunately the last Statement will give me an error ORA-00955
If I create the PK constraint like it was defined originally with:
ALTER TABLE B ADD CONSTRAINT PK_B PRIMARY KEY ("TYP", "NR", "HH");
everything works fine.
Perhaps there is an INDEX associated with the PRIMARY KEY CONSTRAINT, and it is also named as PK_B.
You can check it as :
SELECT * FROM USER_INDEXES WHERE TABLE_NAME='<table_name>';
If that's true, then do :
ALTER INDEX "PK_B" RENAME TO "PK_XYZ";
Update : Regarding ALTER INDEX statement, few important points as mentioned by Justin in the comments
Oracle implicitly creates an UNIQUE index to support the PRIMARY KEY CONSTRAINT. Since, the index is of the same name that of the primary key, and now that the primary key is being modified, it is better to drop and re-create the index again as per the definition of the old primary key.
My conclusion :
The primary key constraint is enforced through a unique index.
If Oracle already finds an index – unique or non-unique – it uses it
for the primary key.
If the index was initially created as non-unique, it will continue to
show as non-unique, however it will actually be a unique index.
A good demonstration and quite detailed on other aspects too, by Arup : Primary Keys Guarantee Uniqueness? Think Again.
I had the same issue where I had to do the following to delete reference to a table from the view whilst recreating the database from the scratch. I was searching for the same in tables and indexes first.
connect sys/oracle as sysdba;
select * from all_tables
select * from all_indexes
(finally located the reference in the views)
select * from all_views where view_name like '%WKSTSTATE%';
drop view RUEGEN.WKSTSTATE;
This question is a duplicate in meaning still I have to clarify it. Oracle documentation specifically says that it is possible to specify a primary key in CREATE VIEW clause(11g docs has the same notion) . Yet when I try to do it like this:
create or replace view ABC(A, B, C, CONSTRAINT A_PK PRIMARY KEY (A)) ....
I get ORA-00922: missing or invalid option pointing at "primary key" phrase. The question is, is it me or is it something wrong with Oracle Documentation?
The simple answer is that your syntax is incorrect. You must specify DISABLE.
NOVALIDATE disables validation of the primary key, in a view this is the default and so is included automatically; but it's a lot clearer if you use it, as, in a lovely double negative, disable novalidate disables the ability to disable the primary key.
The rely is optional; it specifies whether to take into account the primary key when creating the view. The antonym of rely is norely.
There are a lot of restrictions on creating a view constraint and as it relies on the table below it's not really worth it as already noted by #RC. But if you need it for documentation only then here you go:
SQL> create table tmp_test ( a number(10), b varchar2(120) );
Table created.
SQL>
SQL> insert into tmp_test
2 select level, 'b'
3 from dual
4 connect by level <= 20
5 ;
20 rows created.
SQL> commit ;
Commit complete.
SQL>
SQL> alter table tmp_test
2 add constraint tmp_test_pk
3 primary key (a)
4 using index;
Table altered.
SQL>
SQL> create or replace view v_tmp_test (a, b
2 , constraint v_tmp_test_pk primary key (a) rely disable novalidate) as
3 select a, b
4 from tmp_test
5 ;
View created.
SQL>
From the documentation:
View Constraints
Oracle does not enforce view constraints. However, operations on views
are subject to the integrity constraints defined on the underlying
base tables. This means that you can enforce constraints on views
through constraints on base tables.
Notes on View Constraints View constraints are a subset of table
constraints and are subject to the following restrictions:
You can specify only unique, primary key, and foreign key constraints
on views. However, you can define the view using the WITH CHECK OPTION
clause, which is equivalent to specifying a check constraint for the
view.
View constraints are supported only in DISABLE NOVALIDATE mode. You
cannot specify any other mode. You must specify the keyword DISABLE
when you declare the view constraint. You need not specify NOVALIDATE
explicitly, as it is the default.
The RELY and NORELY parameters are optional. View constraints, because
they are unenforced, are usually specified with the RELY parameter to
make them more useful. The RELY or NORELY keyword must precede the
DISABLE keyword. Please refer to "RELY Clause" for more information.
Because view constraints are not enforced directly, you cannot specify
INITIALLY DEFERRED or DEFERRABLE.
You cannot specify the using_index_clause, the exceptions_clause
clause, or the ON DELETE clause of the references_clause.
You cannot define view constraints on attributes of an object column.
You have to create the constraint as disabled. It is really a way to give a hint to the optimizer to influence the query plan.
Data integrity is enforced at the underlying table level. When you think about it, enforcing a primary key constraint at the view level doesn't make a whole lot of sense. A plain ole view doesn't store data, it is just a "view" of data provided by other tables. If a primary key constraint was only placed on a view of some underlying table and that table does not enforce the constraint itself, how would the view handle the situation where someone updated the table directly with data that breaks it's constraint? (i.e. the table has no idea what constraints are placed on it via a view)
I have a database which has a NOT NULL constraint on a field, and I want to remove this constraint. The complicating factor is that this constraint has a system-defined name, and that constraint's name differs between the production server, integration server, and the various developer databases. Our current process is to check in change scripts, and an automated task executes the appropriate queries through sqlplus against the target database, so I'd prefer a solution that could just be sent straight into sqlplus.
On my own database, the SQL to drop this would be:
alter table MYTABLE drop constraint SYS_C0044566
I can see the constraint when I query the all_constraints view:
select * from all_constraints where table_name = 'MYTABLE'
but I am not sure how to work with the SEARCH_CONDITION's LONG data type or how best to dynamically delete the looked-up constraint even after I know its name.
So, how can I create a change script that can drop this constraint based on what it is, rather than what its name is?
EDIT:
#Allan's answer is a good one, but I am concerned (in my lack of Oracle expertise) that it may not be universally true that any constraint that might have a system-generated name will have associated with it a way to remove the constraint without having to know its name. Is it true that there will always be a way to avoid having to know a system-named constraint's name when logically dropping that constraint?
alter table MYTABLE modify (MYCOLUMN null);
In Oracle, not null constraints are created automatically when not null is specified for a column. Likewise, they are dropped automatically when the column is changed to allow nulls.
Clarifying the revised question: This solution only applies to constraints created for "not null" columns. If you specify "Primary Key" or a check constraint in the column definition without naming it, you'll end up with a system-generated name for the constraint (and the index, for the primary key). In those cases, you'd need to know the name to drop it. The best advice there is to avoid the scenario by making sure you specify a name for all constraints other than "not null". If you find yourself in the situation where you need to drop one of these constraints generically, you'll probably need to resort to PL/SQL and the data-definition tables.
Try:
alter table <your table> modify <column name> null;
Just remember, if the field you want to make nullable is part of a primary key, you can't.
Primary Keys cannot have null fields.
To discover any constraints used, use the code below:
-- Set the long data type for display purposes to 500000.
SET LONG 500000
-- Define a session scope variable.
VARIABLE output CLOB
-- Query the table definition through the <code>DBMS_METADATA</code> package.
SELECT dbms_metadata.get_ddl('TABLE','[Table Described]') INTO :output FROM dual;
This essentially shows a create statement for how the referenced table is made. By knowing how the table is created, you can see all of the table constraints.
Answer taken from Michael McLaughlin's blog: http://michaelmclaughlin.info/db1/lesson-5-querying-data/lab-5-querying-data/ From his Database Design I class.
I was facing the same problem trying to get around a custom check constraint that I needed to updated to allow different values. Problem is that ALL_CONSTRAINTS does't have a way to tell which column the constraint(s) are applied to. The way I managed to do it is by querying ALL_CONS_COLUMNS instead, then dropping each of the constraints by their name and recreate it.
select constraint_name
from all_cons_columns
where table_name = [TABLE_NAME]
and column_name = [COLUMN_NAME];
Something like that happened to me when I made copies of structures to temporary tables, so I removed the not null.
DECLARE
CURSOR cur_temp_not_null IS
SELECT table_name, constraint_name FROM all_constraints WHERE table_name LIKE 'TEMP_%' AND owner='myUSUARIO';
V_sql VARCHAR2(200);
BEGIN
FOR c_not_null IN cur_temp_not_null
LOOP
v_sql :='ALTER TABLE ' || c_not_null.table_name || ' DROP CONSTRAINT '|| c_not_null.constraint_name;
EXECUTE IMMEDIATE v_sql;
END LOOP;
END;
If constraint on column STATUS was created without a name during creating a table, Oracle will assign a random name for it. Unfortunately, we cannot modify the constraint directly.
Steps involved of dropping unnamed constraint linked to column STATUS
Duplicate STATUS field into a new field STATUS2
Define CHECK constraints on STATUS2
Migrate data from STATUS into STATUS2
Drop STATUS column
Rename STATUS2 to STATUS
ALTER TABLE MY_TABLE ADD STATUS2 NVARCHAR2(10) DEFAULT 'OPEN';
ALTER TABLE MY_TABLE ADD CONSTRAINT MY_TABLE_CHECK_STATUS CHECK (STATUS2 IN ('OPEN', 'CLOSED'));
UPDATE MY_TABLE SET STATUS2 = STATUS;
ALTER TABLE MY_TABLE DROP COLUMN STATUS;
ALTER TABLE MY_TABLE RENAME COLUMN STATUS2 TO STATUS;