How can I store NULLs in NOT NULL field? - oracle

I just came across NULL values in NOT-NULL fields in our test database. How could they get there? I know that NOT-NULL constraints can be altered with NOVALIDATE clause, but that would change table's last_ddl_time in USER_OBJECTS. And that time is less than the date that those records were created. Is there something else I'm overlooking? Or is that someone's manual work for sure?
Table is partitioned and index-organized if that is relevant.
Oracle version is 9.2

The NOT NULL column condition is not like other constraints: you can disable a NOT NULL constraint but the column won't be considered NOT NULL if you reenable the constraint with NOVALIDATE. Let's build a small example:
SQL> CREATE TABLE tt (ID NUMBER NOT NULL);
Table created
SQL> SELECT column_name, nullable FROM user_tab_columns WHERE table_name = 'TT';
COLUMN_NAME NULLABLE
------------------------------ --------
ID N
now if I disable the constraint and reenable it with NOVALIDATE, the column won't be considered NOT NULLABLE by Oracle:
SQL> SELECT constraint_name, search_condition
2 FROM user_constraints WHERE table_name = 'TT';
CONSTRAINT_NAME SEARCH_CONDITION
------------------------------ ----------------------------
SYS_C00786538 "ID" IS NOT NULL
SQL> ALTER TABLE tt MODIFY CONSTRAINT SYS_C00786538 DISABLE;
Table altered
SQL> ALTER TABLE tt MODIFY CONSTRAINT SYS_C00786538 ENABLE NOVALIDATE;
Table altered
SQL> SELECT column_name, nullable FROM user_tab_columns WHERE table_name = 'TT';
COLUMN_NAME NULLABLE
------------------------------ --------
ID Y
So, I would say that if you have NULL values in a NOT NULLABLE column (as per my last query) you have a bug (contact support?)

Check If the constraint are suspended / Disabled

And you're sure these columns are really null? In other words:
SELECT field
FROM your_table
WHERE not_null_field IS NULL;
returns rows? Perhaps they've just got non-displayable data...

Related

Dropping a NOTNULL contraint results in a full table scan

I have a table that contains about 25 million records and has some NOTNULL-constraint on several fields.
When i drop one of these NOTNULL contraints a full table scan is executed (which takes quite a lot of time). I can see that in the session browser of a second instance of TOAD (i use TOAD to drop the constraint).
Is there a way to avoid this full table scan when a constraint gets dropped?
This suggests the column causing the full table scan has a default value, possibly from adding the column to the table while data already existed, with a not-null constraint (which is only possibly since 11gR1).
As a demo, without a default value:
create table t42 (id number);
alter table t42 add (some_col number not null);
select data_default, default_length from user_tab_columns where column_name = 'SOME_COL';
DATA_DEFAULT DEFAULT_LENGTH
------------------------------------------------------------ --------------
insert into t42 (id, some_col)
select level, 0 from dual
connect by level <= 100000;
insert into t42 (id, some_col)
select 100000 + level, 1 from dual
connect by level <= 10000;
select some_col, count(*) from t42 group by some_col;
SOME_COL COUNT(*)
---------- ----------
1 10000
0 100000
set timing on
alter table t42 modify (some_col null);
Table T42 altered.
Elapsed: 00:00:00.056
But with a default value:
create table t42 (id number);
insert into t42 (id)
select level from dual
connect by level <= 100000;
alter table t42 add (some_col number default 0 not null);
select data_default, default_length from user_tab_columns where column_name = 'SOME_COL';
DATA_DEFAULT DEFAULT_LENGTH
------------------------------------------------------------ --------------
0 2
insert into t42 (id, some_col)
select 100000 + level, 1 from dual
connect by level <= 10000;
select some_col, count(*) from t42 group by some_col;
SOME_COL COUNT(*)
---------- ----------
1 10000
0 100000
set timing on
alter table t42 modify (some_col null);
Table T42 altered.
Elapsed: 00:00:04.734
Now the alter takes much longer, because it has to actually update all the pre-constraint rows to physically have a value zero. After the alter you see the same data, even if you change the default value (before or after the original alter; though if you do it before you potentially have a small window where a constraint violation could occur):
alter table t42 modify (some_col default null);
select data_default, default_length from user_tab_columns where column_name = 'SOME_COL';
DATA_DEFAULT DEFAULT_LENGTH
------------------------------------------------------------ --------------
null 4
select some_col, count(*) from t42 group by some_col;
SOME_COL COUNT(*)
---------- ----------
1 10000
0 100000
There isn't really any way around this, other than adding a new column without the default (which will take at least as long, and probably cause other side-effects).
Notice that the default value has change from not being set at all to explicitly being null. When you insert a row there isn't any practical difference - the column value ends up null either way - but you can't completely remove the default once it's been set.
It's also interesting that if you change the column default without dropping the constraint, that has no effect on the reported value for the rows that were using it - they would still show as zero. Oracle seems to be storing that constraint default somewhere else, which makes sense.
Any rows inserted after the default/not-null column was added will have an actual value stored in the table anyway, and changing the default will affect subsequent insertions - but the rows that already existed before the constraint was added behave as if they had actually been updated to whatever default value was specified when the constraint was added.
This change in 11g was mainly to speed the column addition up, and stop you having to to separate steps to add the column without a constraint, then update all existing rows (which was the slow bit), and then altering the table again to add the constraint. This mechanism lets you do it (almost) instantly with just a metadata change. But that update cost still has to be pain if the constraint is then removed.

How to delete the primary key from without using constraint name

CREATE TABLE Persons (
ID int PRIMARY KEY,
LastName varchar(255) NOT NULL,
FirstName varchar(255),
Age int
);
How to remove the primary key as there is no constraint defined?
"How to remove PK as there is no constraint defined?"
Actually it's every bit as simple as you might hope:
SQL> create table t23 (id number primary key);
Table created.
SQL> select constraint_name, constraint_type
2 from user_constraints
3 where table_name = 'T23'
4 /
CONSTRAINT_NAME C
------------------------------ -
SYS_C0034419 P
SQL> alter table t23 drop primary key;
Table altered.
SQL> select constraint_name, constraint_type
2 from user_constraints
3 where table_name = 'T23'
4 /
no rows selected
SQL>
run this to get constraint name :
SELECT *
FROM user_cons_columns
WHERE table_name = Persons;
then run
ALTER TABLE Persons
DROP CONSTRAINT <pk constraint>;
Don't think you can do it in 1 SQL command,without knowing the constraint name, but you can know the constraint name as in this case it would be defined by system. Something which starts with SYS.....
Alternatively you can use a PLSQL block to achieve the same.
See the example below for your case.
CREATE TABLE Persons (
ID int PRIMARY KEY,
LastName varchar(255) NOT NULL,
FirstName varchar(255),
Age int
);
Find constraint name
select CONSTRAINT_NAME
From USER_CONSTRAINTS
where table_name='PERSONS'
AND CONSTRAINT_TYPE='P';
OUTPUT:= SYS_C0012152
Drop Constraint
ALTER TABLE PERSONS
DROP CONSTRAINT SYS_C0012152;
Note: The constraint name SYS_C0012152 is not enclosed in single quotes.
PLSQL Block to do the same
declare
sql_stmt varchar2(255);
cons_name varchar2(30);
begin
select CONSTRAINT_NAME
into cons_name
From USER_CONSTRAINTS
where table_name='PERSONS'
AND CONSTRAINT_TYPE='P';
sql_stmt:=' ALTER TABLE PERSONS
DROP CONSTRAINT '||cons_name;
dbms_output.put_line(sql_stmt);
execute immediate sql_stmt;
end;

Validate ALL the constraints when inserting a record in Oracle [duplicate]

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.

How to see the default values in a table in oracle

SQL> alter table dept modify (deptno number(2) default 10);
how to find out whether a column in a table contains a default value or not.how to see the default values in a table in oracle
Use the metadata table ALL_TAB_COLUMNS (or USER_TAB_COLUMNS if the table in question is on your current schema):
select DATA_DEFAULT
from all_tab_columns
where table_name='xxx' and column_name='yyy'

How to remove constraint based on columns from oracle database?

Is there is a way to remove a constraint (unique index) based on columns name?
What I would like to do is to remove a constraint where columna name is name, and name_type.
ALTER TABLE MY_TABLE DROP CONSTRAINT NAME_OF_CONSTRAINT;
I don't have a name so I would like to do it this way...
ALTER TABLE MY_TABLE DROP CONSTRAINT **WHERE COLUMN = col1 AND column = col2**
Any syntax to do something like this on a constraint.
I didn't think this was possible with a single statement, but it turns out it is, as shown in the examples in the documentation:
ALTER TABLE MY_TABLE DROP UNIQUE(col1, col2);
A complete example:
ALTER TABLE MY_TABLE ADD UNIQUE (col1, col2);
Table my_table altered.
SELECT CONSTRAINT_NAME, INDEX_NAME
FROM USER_CONSTRAINTS
WHERE TABLE_NAME = 'MY_TABLE';
CONSTRAINT_NAME INDEX_NAME
------------------------------ ------------------------------
SYS_C0092455 SYS_C0092455
ALTER TABLE MY_TABLE DROP UNIQUE(col1, col2);
Table my_table altered.
SELECT CONSTRAINT_NAME, INDEX_NAME
FROM USER_CONSTRAINTS
WHERE TABLE_NAME = 'MY_TABLE';
no rows selected
An alternative approach is to query the USER_CONSTRAINTS and USER_CONS_COLUMNS views to find the matching constraint name - presumably system-generated or you would already know it - and then use that name. If you need to do this as a script then you could query in a PL/SQL block, and plug the found constraint name into a dynamic ALTER TABLE statement.
Do a SELECT * FROM USER_CONS_COLUMNS or ALL_CONS_COLUMNS. This will give you the constraint name for the owner, table and column combination.
Out of the multiple rows returned for a column name, use the constraint names as necessary in the ALTER TABLE ... DROP CONSTRAINT... syntax.
Use dynamic sql to do it for all rows in a loop if you are absolutely sure that you can drop all of them.
This will give you an extra layer of protection so that you don't accidentally drop a constraint that was needed.

Resources