Learn more about the nature of a constraint? - sql

A piece of legacy code I'm running that does some oracle SQL is violating a constraint called REF_REQUEST.
I look up this constraint by doing:
select * from all_constraints where constraint_name='REF_REQUEST'
This tells me that the constraint_type is 'R', and it gives me the table name of CORRESPONDENCE.
However, I still don't know... what value I'm missing from CORRESPONDENCE, or where I'm trying to insert that is causing the issue, and what column is relevant from each. How can I learn this information by querying the dB?

Alternatively, you know it's a referential integrity constraint based on your select from all_constraints (constraint_type = R), so you can just query all_cons_columns for your answer:
SELECT table_name, column_name
FROM all_cons_columns
WHERE constraint_name = 'REF_REQUEST'
ORDER by position;

Use get_ddl method in dbms_metadata package to get more details about the constraint
SELECT CAST(DBMS_METADATA.GET_DDL('CONSTRAINT','REF_REQUEST','CERTIFICATION') AS VARCHAR2(4000))
FROM DUAL
That should tell you what columns the constraint is acting on
One of the overloaded methods accepts the schema name, so you can pass it as a parameter.
DBMS_METADATA.GET_DDL (
object_type IN VARCHAR2,
name IN VARCHAR2,
schema IN VARCHAR2 DEFAULT NULL,
version IN VARCHAR2 DEFAULT 'COMPATIBLE',
model IN VARCHAR2 DEFAULT 'ORACLE',
transform IN VARCHAR2 DEFAULT 'DDL')
RETURN CLOB;
Further reading:
Oracle documentation on dbms_metadata.get_xxx subprograms

Related

Can I alter the constraints of one table by using the constraints of another table?

I had to drop a table and remake it using an archive. In the process, I lost the table's constraints--things like the primary key--triggers, indices, and more. I have, however, the same table on a different DB, which has all the appropriate constraints.
I have tried adding the constraints, triggers, and indices manually, but there are just too many.
I was wondering if I could do something like:
alter table t73
modify col_n....col_n+1
using (select constraints from t73#otherdb)
No, that won't work.
What you could do is to use some GUI (like TOAD or SQL Developer), find table t73, have a look at its Script which contains all commands (CREATE TABLE, CREATE INDEX, CREATE CONSTRAINT, ...) and copy/paste the ones you need and execute them in your current database.
That would be quick.
If you want to do it right (you know, pretending you know what you're doing, just like I do), then see DBMS_METADATA.GET_DDL and extract those commands from the database.
The final result should be the same.
this is below example how you can use dbms_metadata.get_ddl oracle package
create table EX_EMPLOYEe ( id number(5) null, name varchar2(100))
/
alter table ex_Employee add constraint PK_EX_EMPLOYEE primary key (id)
/
alter table ex_Employee add constraint FK_EX_EMPLOYEE foreign key (id)
references ex_Employee1 (id)
/
create table EX_EMPLOYEe1 ( id number(5) null, name varchar2(100))
/
alter table ex_Employee1 add constraint PK_EX_EMPLOYEE1 primary key (id)
alter table SYS_PARAM_KEY_LABEL
add constraint FK1_SYS_PARAM_KEY_LABEL foreign key (KEY_GROUP_ID)
references SYS_PARAM_KEY_GROUP (KEY_GROUP_ID);
/
CREATE INDEX IDX_EX_EMPLOYEe on ex_employee(name)
/
Create or replace PROCEDURE P_EX_EMPLOYEe as
begin
select id from ex_employee where rownum=1;
end;
/
CREATE OR REPLACE TRIGGER TRG_EX_EMPLOYEe AFTER DELETE ON EX_EMPLOYEe
FOR EACH ROW
BEGIN
DELETE FROM ex_employee1 WHERE id = :OLD.ID;
END;
/
select to_char( dbms_metadata.get_ddl('CONSTRAINT', c.constraint_name)) from user_constraints c where table_name='EX_EMPLOYEE'
and c.constraint_type='P'
union
select to_char( dbms_metadata.get_ddl('REF_CONSTRAINT', c.constraint_name)) from user_constraints c where table_name='EX_EMPLOYEE'
and c.constraint_type='R'
union
select to_char( dbms_metadata.get_ddl('INDEX', c.index_name)) from user_indexes c where table_name='EX_EMPLOYEE'
union
select to_char( dbms_metadata.get_ddl('PROCEDURE', d.name)) from user_dependencies d where d.referenced_name='EX_EMPLOYEE'
and d.type='PROCEDURE'
union
select to_char( dbms_metadata.get_ddl('TRIGGER', d.name)) from user_dependencies d where d.referenced_name='EX_EMPLOYEE'
and d.type='TRIGGER'

To show the syntax of created table [duplicate]

This question already has answers here:
`show create table` equivalent in oracle sql
(6 answers)
Closed 5 years ago.
i am trying to show the syntax of a created table as described below
here is the sturucture of the table:
i need the complete syntax of table creation as:
CREATE TABLE STUDENT (R_NUMBER NUMBER(4) PRIMARY KEY NOT NULL,NAME VARCHAR2(20) NOT NULL,F_NAME VARCHAR2(20) NOT NULL, M_NAME VARCHAR2(20) NOT NULL, ADMN_NO REFERENCES FEE NUMBER(4);
Is there any data dictionary through which I can get this output?
Thanks in advance
I would try dbms_metadata.get_ddl using something like
SELECT dbms_metadata.get_ddl('TABLE', 'STUDENT', 'MYSCHEMA') -- Replace MYSCHEMA with your actual schema name
FROM dual
you can query data dictionary views user_tab_columns and user_cons_columns joined :
select c.column_name, c.data_type, c.nullable, s.constraint_name, c.data_length
from user_tab_columns c , user_cons_columns s
where c.table_name = 'AB'
and s.column_name(+) = c.column_name
and s.table_name(+) = c.table_name;

Multiple constraints in table: How to get all violations?

I have a table in Oracle with several constraints. When I insert a new record and not all constraints are valid, then Oracle raise only the "first" error. How to get all violations of my record?
CREATE TABLE A_TABLE_TEST (
COL_1 NUMBER NOT NULL,
COL_2 NUMBER NOT NULL,
COL_3 NUMBER NOT NULL,
COL_4 NUMBER NOT NULL
);
INSERT INTO A_TABLE_TEST values (1,null,null,2);
ORA-01400: cannot insert NULL into ("USER_4_8483C"."A_TABLE_TEST"."COL_2")
I would like to get something like this:
Column COL_2: cannot insert NULL
Column COL_3: cannot insert NULL
This would be also sufficient:
Column COL_2: not valid
Column COL_3: not valid
Of course I could write a trigger and check each column individually, but I like to prefer constraints rather than triggers, they are easier to maintain and don't require manually written code.
Any idea?
There no straightforward way to report all possible constraint violations. Because when Oracle stumble on first violation of a constraint, no further evaluation is possible, statement fails, unless that constraint is deferred one or the log errors clause has been included in the DML statement. But it should be noted that log errors clause won't be able to catch all possible constraint violations, just records first one.
As one of the possible ways is to:
create exceptions table. It can be done by executing ora_home/rdbms/admin/utlexpt.sql script. The table's structure is pretty simple;
disable all table constraints;
execute DMLs;
enable all constraints with exceptions into <<exception table name>> clause. If you executed utlexpt.sql script, the name of the table exceptions are going to be stored would be exceptions.
Test table:
create table t1(
col1 number not null,
col2 number not null,
col3 number not null,
col4 number not null
);
Try to execute an insert statement:
insert into t1(col1, col2, col3, col4)
values(1, null, 2, null);
Error report -
SQL Error: ORA-01400: cannot insert NULL into ("HR"."T1"."COL2")
Disable all table's constraints:
alter table T1 disable constraint SYS_C009951;
alter table T1 disable constraint SYS_C009950;
alter table T1 disable constraint SYS_C009953;
alter table T1 disable constraint SYS_C009952;
Try to execute the previously failed insert statement again:
insert into t1(col1, col2, col3, col4)
values(1, null, 2, null);
1 rows inserted.
commit;
Now, enable table's constraints and store exceptions, if there are any, in the exceptions table:
alter table T1 enable constraint SYS_C009951 exceptions into exceptions;
alter table T1 enable constraint SYS_C009950 exceptions into exceptions;
alter table T1 enable constraint SYS_C009953 exceptions into exceptions;
alter table T1 enable constraint SYS_C009952 exceptions into exceptions;
Check the exceptions table:
column row_id format a30;
column owner format a7;
column table_name format a10;
column constraint format a12;
select *
from exceptions
ROW_ID OWNER TABLE_NAME CONSTRAINT
------------------------------ ------- ------- ------------
AAAWmUAAJAAAF6WAAA HR T1 SYS_C009951
AAAWmUAAJAAAF6WAAA HR T1 SYS_C009953
Two constraints have been violated. To find out column names, simply refer to user_cons_columns data dictionary view:
column table_name format a10;
column column_name format a7;
column row_id format a20;
select e.table_name
, t.COLUMN_NAME
, e.ROW_ID
from user_cons_columns t
join exceptions e
on (e.constraint = t.constraint_name)
TABLE_NAME COLUMN_NAME ROW_ID
---------- ---------- --------------------
T1 COL2 AAAWmUAAJAAAF6WAAA
T1 COL4 AAAWmUAAJAAAF6WAAA
The above query gives us column names, and rowids of problematic records. Having rowids at hand, there should be no problem to find those records that cause constraint violation, fix them, and re-enable constraints once again.
Here is the script that has been used to generate alter table statements for enabling and disabling constraints:
column cons_disable format a50
column cons_enable format a72
select 'alter table ' || t.table_name || ' disable constraint '||
t.constraint_name || ';' as cons_disable
, 'alter table ' || t.table_name || ' enable constraint '||
t.constraint_name || ' exceptions into exceptions;' as cons_enable
from user_constraints t
where t.table_name = 'T1'
order by t.constraint_type
You would have to implement a before-insert trigger to loop through all the conditions that you care about.
Think about the situation from the database's perspective. When you do an insert, the database can basically do two things: complete the insert successfully or fail for some reason (typically a constraint violation).
The database wants to proceed as quickly as possibly and not do unnecessary work. Once it has found the first complaint violation, it knows that the record is not going into the database. So, the engine wisely returns an error and stops checking further constraints. There is no reason for the engine to get the full list of violations.
In the meantime I found a lean solution using deferred constraints:
CREATE TABLE A_TABLE_TEST (
COL_1 NUMBER NOT NULL DEFERRABLE INITIALLY DEFERRED,
COL_2 NUMBER NOT NULL DEFERRABLE INITIALLY DEFERRED,
COL_3 NUMBER NOT NULL DEFERRABLE INITIALLY DEFERRED,
COL_4 NUMBER NOT NULL DEFERRABLE INITIALLY DEFERRED
);
INSERT INTO A_TABLE_TEST values (1,null,null,2);
DECLARE
CHECK_CONSTRAINT_VIOLATED EXCEPTION;
PRAGMA EXCEPTION_INIT(CHECK_CONSTRAINT_VIOLATED, -2290);
REF_CONSTRAINT_VIOLATED EXCEPTION;
PRAGMA EXCEPTION_INIT(REF_CONSTRAINT_VIOLATED , -2292);
CURSOR CheckConstraints IS
SELECT TABLE_NAME, CONSTRAINT_NAME, COLUMN_NAME
FROM USER_CONSTRAINTS
JOIN USER_CONS_COLUMNS USING (TABLE_NAME, CONSTRAINT_NAME)
WHERE TABLE_NAME = 'A_TABLE_TEST'
AND DEFERRED = 'DEFERRED'
AND STATUS = 'ENABLED';
BEGIN
FOR aCon IN CheckConstraints LOOP
BEGIN
EXECUTE IMMEDIATE 'SET CONSTRAINT '||aCon.CONSTRAINT_NAME||' IMMEDIATE';
EXCEPTION
WHEN CHECK_CONSTRAINT_VIOLATED OR REF_CONSTRAINT_VIOLATED THEN
DBMS_OUTPUT.PUT_LINE('Constraint '||aCon.CONSTRAINT_NAME||' at Column '||aCon.COLUMN_NAME||' violated');
END;
END LOOP;
END;
It works with any check constraint (not only NOT NULL). Checking FOREIGN KEY Constraint should work as well.
Add/Modify/Delete of constraints does not require any further maintenance.

Use function as default value for column in Oracle11g

We are testing out Oracle at my work and im in charge of building all of the database objects (tables, procs, trigger, etc) on Oracle, and we currently use Microsoft SQL Server 2008 R2. We use uniqueidentifier's for almost all of our ID column's. I used this function to create GUID's:
CREATE OR REPLACE FUNCTION NEWID RETURN CHAR IS guid CHAR(36) ;
BEGIN
SELECT SYS_GUID() INTO guid FROM DUAL;
guid :=
SUBSTR(guid, 1, 8) ||
'-' || SUBSTR(guid, 9, 4) ||
'-' || SUBSTR(guid, 13, 4) ||
'-' || SUBSTR(guid, 17, 4) ||
'-' || SUBSTR(guid, 21);
RETURN guid;
END NEWID;
/
But now I cant figure out how to use it as the default value on columns when creating tables. Here is a non-working example:
CREATE TABLE "NonWorkingExample"
(
"ID" CHAR(36) NOT NULL DEFAULT NEWID(),
"UnitNumber" NUMBER(38) NOT NULL,
"StartDateTime" TIMESTAMP NOT NULL,
"EndDateTime" TIMESTAMP NULL,
CONSTRAINT PK_RentalAgreements PRIMARY KEY ("ID")
);
And the error:
Error starting at line 1 in command:
CREATE TABLE "NonWorkingExample"
(
"ID" CHAR(36) NOT NULL DEFAULT NEWID(),
"UnitNumber" NUMBER(38) NOT NULL,
"StartDateTime" TIMESTAMP NOT NULL,
"EndDateTime" TIMESTAMP NULL,
CONSTRAINT PK_RentalAgreements PRIMARY KEY ("ID")
)
Error at Command Line:3 Column:58
Error report:
SQL Error: ORA-00907: missing right parenthesis
00907. 00000 - "missing right parenthesis"
*Cause:
*Action:
Any help would be much appreciated. Thank you.
I completely agree with the other answers that if you really want to call a function, you'll need to use a trigger or you'll need to embed the function call in the INSERT statement itself. Embedding the function call in the INSERT statement is more efficient than forcing Oracle to execute a trigger for every row that you insert.
I should point out, though, that you can use SYS_GUID() alone as the default value for a column without worrying about a trigger
SQL> create table foo (
2 col1 varchar2(32) default sys_guid(),
3 col2 number
4 );
Table created.
SQL> insert into foo( col2 ) values( 1 );
1 row created.
SQL> select * from foo;
COL1 COL2
-------------------------------- ----------
7B64E8AE7404421C80A590F65873CD79 1
Do you really need the extra dashes in your GUID values? Could you potentially add the dashes only when you display the data? Or, since you're on 11g, add a function-based virtual column that converts the dash-free GUID to a GUID in the format that you prefer?
And since you're coming from SQL Server, I should point out that it would be conventional in Oracle to use a sequence to populate synthetic primary keys rather than using a GUID. Using a sequence to populate the key would generally be more efficient than calling SYS_GUID.
Suggestion:
Use triggers instead.
For example:
CREATE TRIGGER SetGUIDforTableXYZ BEFORE INSERT ON TableXYZ
FOR EACH ROW
BEGIN
:new.ID := NEWID();
END;
That should do the trick (assuming I haven't messed up the syntax somewhere).
As #Kerri implied, you cannot use a PLSQL function as a default value in a table definition. The relevant statement in the Oracle documentation is "A DEFAULT expression cannot contain references to PL/SQL functions...".
In addition to being the only way to insert a formatted GUID, as you're attempting to do, a trigger provides a second advantage: unlike with a default value, the value set by the trigger cannot be overridden by a careless developer.
As an aside, you should really revise your newid function to use direct assignment rather than select ... from dual:
CREATE OR REPLACE FUNCTION NEWID RETURN CHAR IS
guid VARCHAR(36);
BEGIN
guid := SYS_GUID();
guid :=
SUBSTR(guid, 1, 8) ||
'-' || SUBSTR(guid, 9, 4) ||
'-' || SUBSTR(guid, 13, 4) ||
'-' || SUBSTR(guid, 17, 4) ||
'-' || SUBSTR(guid, 21);
RETURN guid;
END NEWID;
/
Finally, I would advise you to use varchar2 instead of char. Contrary to popular opinion, there is no storage or efficiency advantage to char, so you might as well use varchar2 for everything, just for simplicity's sake.
You can't use a function for this. You don't see it because your syntax is wrong (DEFAULT before CONSTRAINTS). You should have:
"ID" CHAR(36) DEFAULT NEWID() NOT NULL ,
at which point you would receive the following message (11g):
SQL> create table tt (id varchar2(36) default newid() not null);
create table tt (id varchar2(36) default newid() not null)
*
ERROR at line 1:
ORA-04044: procedure, function, package, or type is not allowed here
SQL>
As #Kerri states, it'll take a trigger to do this automatically.
How about removing the function and trigger dependencies by trying something like this:
CREATE TABLE "NonWorkingExample"
(
"ID" CHAR(36) NOT NULL DEFAULT regexp_replace((rawtohex(sys_guid()), '([A-F0-9]{8})([A-F0-9]{4})([A-F0-9]{4})([A-F0-9]{4})([A-F0-9]{12})', '\1-\2-\3-\4-\5'),
"UnitNumber" NUMBER(38) NOT NULL,
"StartDateTime" TIMESTAMP NOT NULL,
"EndDateTime" TIMESTAMP NULL,
CONSTRAINT PK_RentalAgreements PRIMARY KEY ("ID")
)
If Oracle SYS_GUID()'s somewhat similar/sequential GUIDs bother you, you can wrap java.util.UUID.randomUUID() in a pl/sql function to get less predictable values:
CREATE OR REPLACE FUNCTION Java_Util_Random_UUID RETURN VARCHAR2 AS
language java
name 'java.util.UUID.randomUUID() return String';

Displaying the constraints in a table

Hello I am trying to display the constraints in one of my tables but for some reason I get the message no rows selected. Noted below is the table I have created.
Create table Teams (
TeamID varCHAR2(4) constraint Teams_TeamID_PK Primary Key,
TeamName VARCHAR2(40)
);
This is the code I am using to show my constraints.
SELECT constraint_name,
constraint_type,
search_condition
FROM USER_CONSTRAINTS
WHERE table_name = 'Teams';
I am a rookie so I want to make sure I understand what is wrong. I have tried to drop the table thinking that my constraints did not take - I did not, nor did I receive any errors when I created the table and I am referencing TeamID in another table. So when I try to drop the table I get an error message when is what I was hoping for.
Try this:
SELECT constraint_name,
constraint_type,
search_condition
FROM USER_CONSTRAINTS
WHERE table_name = 'TEAMS';
Unless double-quoted when created, all object names in Oracle are upper case.
I personally use:
SELECT * FROM all_constraints WHERE Table_Name = <TableName>;
Use the following code:
show create table table_name;
select dbms_mview.get_ddl('TABLE',USER,'TEAMS') from dual;
If you prefer the CamelCase names, your create table script should have been:
Create table "Teams" (
"TeamID" varCHAR2(4) constraint "Teams_TeamID_PK" Primary Key,
"TeamName" VARCHAR2(40)
);
Without double-quotes Oracle helpfully converts all identifiers to uppercase :)
Type the table name in upper case in where clause within the single quotes.
e.g. WHERE table_name = 'TEAMS';