Oracle - Check constraint on multiple columns - sql

I am trying to add a check constraint on multiple columns in Oracle table that restricts user from inserting NULL into 3 columns simultaneously. However each column in the table can accept NULL independently but not 3 of the columns together.
ALTER TABLE table1 ADD CONSTRAINT CK_not_null
CHECK (col1 IS NOT NULL AND col2 IS NOT NULL AND col3 IS NOT NULL);
This check constraint is not allowing NULL in any of the three columns. Any thought on this?

This constraint will not achieve your needs - it checks that all three columns are not null. The behavior you're describing can be achieved by negating (with the not boolean operator) a condition where all three columns are null:
ALTER TABLE table1
ADD CONSTRAINT
ck_not_null CHECK
(NOT (col1 IS NULL AND col2 IS NULL AND col3 IS NULL))

Related

Recently attended one interview. Unable to answer this one, can you help me out

Let's say there are two tables names like
Table-A has two columns col1,col2.
Table-B has two columns col3,col4.
col1 is primary key, col3 is foreign key dependent on col1. col4 is primary key, col2 is foreign key dependent on col4. So, both the tables are dependent on each other.
Now, I want to insert a record into table-A. How can I do this (in oracle 11g)?
You can defer the constraint evaluation until commit.
ALTER TABLE table_1
ADD CONSTRAINT fk_table_1 (col2 ) REFERENCES table_2( col4)
INITIALLY DEFERRED DEFERRABLE;
This will postpone the constraint checking to the committing time.
The short answer is, you can't. That is, you cannot insert a record into Table_A without also inserting a record into Table_B.
What the interviewer was probably hoping for:
Given that we need to insert records into both tables simultaneously can we do that?
Nope: the insert all syntax will fail on ORA-02291: integrity constraint WHATEVER_FK) violated - parent key not found
insert all
into table_a values (n1, n2)
into table_b values (n2, n1)
select 23 as n1
, 42 as n2
from dual
/
The workaround is to change the foreign keys so they are deferred:
alter table table_a add constraint ab_fk foreign key (col2)
references table_b (col4) INITIALLY DEFERRED DEFERRABLE;
alter table table_b add constraint ba_fk foreign key (col3)
references table_a (col1) INITIALLY DEFERRED DEFERRABLE;
The correct answer (although only recommended for the most assured of candidates):
Why are you asking me questions about a broken data model? Is this indicative of the sort of thing I'll be working with if I join your company?
Because this is a completely false situation. The two foreign keys operate to enforce a 1:1 relationship between table_a and table_b. So why are they two separate tables? Make them one and the problem goes away. This achieves the same thing:
alter table table_a add constraint ab_uk unique (col2);
drop table table_b;
Supposing there is a genuine need for two tables (i.e. if there are more columns than appear in this example) chose one table to be the parent and make the other one the child. Because as soon as we express the model that way we understand why it's so wrong: no child is its own parent, not even in the most twisted of sci-fi tales.
Simple just ensure that Col2 contains a value from Col4 of Table-B and that Col1 doesn't already exist in Table-A. Not knowing anything about the data types of the columns involved does make it more difficult, but since there doesn't seem to be an restrictions on how you get the data this might work:
insert into TableA select max(col1)||'0', max(col2) from TableA ;
This will work as long as col1 is either numeric or character based. It will likely fail if col1 is some other data type.

How to insert into a table considering that table has a Primary Key already in it?

I have two tables A and B and need to insert records (few columns not all) from A into B.
Of course I guess I can do:
INSERT INTO B (col2)
SELECT DISTINCT col2
FROM A
However, Col1 in table B (named ID) has a type of INT so it is causing this error:
Msg 515, Level 16, State 2, Line 1
Cannot insert the value NULL into column 'ID', table 'MyDB.dbo.Visitor'; column does not allow nulls. INSERT fails.
How can I make SQL Server ignore this column and just insert the data I need?
Thanks.
A primary key must fulfill two conditions:
It must be unique in the table (that is, any row in the table can be identified by its primary key), and
The fields that are part of the primary key cannot be NULL. That's because allowing NULL values in the primary key will make the uniqueness of the primary key impossible to hold, because a NULL value is non-equal to any other value, including another NULL.
Quoting from here:
A unique key constraint does not imply the NOT NULL constraint in practice. Because NULL is not an actual value (it represents the lack of a value), when two rows are compared, and both rows have NULL in a column, the column values are not considered to be equal. Thus, in order for a unique key to uniquely identify each row in a table, NULL values must not be used.
This should work assuming you don't insert duplicates into B:
INSERT INTO B (col2)
SELECT DISTINCT col2
FROM A
WHERE col2 IS NOT NULL
Set ID column in table B to "auto-increment".
SQL Server will provide automatically unique values for ID column if you define it as IDENTITY
In your case you can calculate the maximum value of ID column and start IDENTITY from the value that exceeds that maximum.
See the accepted answer for SQL Server, How to set auto increment after creating a table without data loss? for such code.
You need to create a relationship between the two tables and do an update statement.
Update table b set valueb = valuea from table a where a.id = b.id
You also need to rethink your design a little bit it sounds like.

How to add a not null constraint on column containing null values

I have a table with a column that contains a few null values. I want to add a NOT NULL constraint on that column without updating the existing nulls to a non-null value. I want to keep the existing null values and check for future rows that they contain a not null value for this column. Is this possible? How?
You can add an unvalidated constraint - it will not look at existing rows, but it will be checked for any new or updated rows.
ALTER TABLE mytable MODIFY mycolumn NOT NULL NOVALIDATE;
Just be aware that you won't be able to update an existing row unless it satisfies the constraint.
Also, be aware of the downside that the optimizer will not be able to take advantage of this constraint in making its plans - it has to assume that some rows may still have a null.
ALTER TABLE table_name
SET column_name = '0'
WHERE column_name IS NULL;
ALTER TABLE table_name
MODIFY COLUMN(column_name NUMBER CONSTRAINT constraint_identifier NOT NULL);
This is of course assuming that your column is a number but it's the same thing really, you would just change the '0' to a default value that isn't null.
Hammad:
I face the problem and solve like following:
Alter table thr_empl_info modify THR_EM_DESIGNATION_ID not null

sql unique and setting constraints on inserts

I have a database where I need to avoid inserting duplicates. The requirements are:
For the subset of rows with matching column 1, there can not be any that have the same column 2.
For the subset of rows with matching column 1, there can not be any that have the same column 3 and 4.
I'm new to SQL so is there a way of setting these relationships when I create the database (create table) or do I have to do a select and do these checks manually before inserting into the table?
In effect, you need the column 1 and 2 to be unique, and also columns 1,3 and 4 to be unique. So when you create the table, you can use two UNIQUE constaints:
CREATE TABLE tbl (
col1 varchar(255),
col2 varchar(255),
col3 varchar(255),
col4 varchar(255),
CONSTRAINT uc_first UNIQUE(col1, col2),
CONSTRAINT uc_second UNIQUE(col1, col3, col4)
)
Just to get the ball rolling ...
Could you go back immediately after the insert and delete duplicated rows based on the contraints you mentioned?

Field value must be unique unless it is NULL

I'm using SQL Server 2005.
I have a field that must either contain a unique value or a NULL value. I think I should be enforcing this with either a CHECK CONSTRAINT or a TRIGGER for INSERT, UPDATE.
Is there an advantage to using a constraint here over a trigger (or vice-versa)? What might such a constraint/trigger look like?
Or is there another, more appropriate option that I haven't considered?
I create a view with the an index that ignores the nulls through the where clause...i.e. if you insert null into the table the view doesn't care but if you insert a non null value the view will enforce the constraint.
create view dbo.UniqueAssetTag with schemabinding
as
select asset_tag
from dbo.equipment
where asset_tag is not null
GO
create unique clustered index ix_UniqueAssetTag
on UniqueAssetTag(asset_tag)
GO
So now my equipment table has an asset_tag column that allows multiple nulls but only unique non null values.
Note:
If using mssql 2000, you'll need to "SET ARITHABORT ON" right before any insert, update or delete is performed on the table. Pretty sure this is not required on mssql 2005 and up.
Here is an alternative way to do it with a constraint. In order to enforce this constraint you'll need a function that counts the number of occurrences of the field value. In your constraint, simply make sure this maximum is 1.
Constraint:
field is null or dbo.fn_count_maximum_of_field(field) < 2
EDIT I can't remember right now -- and can't check it either -- whether the constraint check is done before the insert/update or after. I think after with the insert/update being rolled back on failure. If it turns out I'm wrong, the 2 above should be a 1.
Table function returns an int and uses the following select to derive it
declare #retVal int
select #retVal = max(occurrences)
from (
select field, count(*) as occurrences
from dbo.tbl
where field = #field
group by field
) tmp
This should be reasonably fast if your column as a (non-unique) index on it.
You can accomplish this by creating a computed column and put the unique index on that column.
ALTER TABLE MYTABLE
ADD COL2 AS (CASE WHEN COL1 IS NULL THEN CAST(ID AS NVARCHAR(255)) ELSE COL1 END)
CREATE UNIQUE INDEX UQ_COL2 ON MYTABLE (COL2)
This is assuming that ID is the PK of your table and COL1 is the "unique or null" column.
The computed column (COL2) will use the PK's value if your "unique" column is null.
There is still the possibility of collisions between the ID column and COL1 in the following example:
ID COL1 COL2
1 [NULL] 1
2 1 1
To get around this I usually create another computed column which stores whether the value in COL2 comes from the ID column or the COL1 column:
ALTER TABLE MYTABLE
ADD COL3 AS (CASE WHEN COL1 IS NULL THEN 1 ELSE 0 END)
The index should be changed to:
CREATE UNIQUE INDEX UQ_COL2 ON MYTABLE (COL2, COL3)
Now the index is on both computed columns COL2 and COL3 so there is no issue:
ID COL1 COL2 COL3
1 [NULL] 1 1
2 1 1 0
In Oracle, a unique key will permit multiple NULLs.
In SQL Server 2005, a good approach is to do your inserts through a view, and disable direct inserts into the table.
Here is some sample code.
Is there a primary key on this table, maybe an Identity column? You could create a unique key that is a composite of the field you are enforcing uniqueness on in combination with the primary key.
There is a discussion about just this kind of issue here: http://blog.sqlauthority.com/2008/09/07/sql-server-explanation-about-usage-of-unique-index-and-unique-constraint/
FYI - SQL Server 2008 introduces filtered indexes which would allow you to approach this a bit differently.
Usually a trigger will allow you to provide a more verbose and explanatory message than a check constraint, so I have used those to avoid the "which column was bad" game in debugging.
A constraint is far lighter than a trigger, even though a unique constraint is effectively an index.
However, you are only allowed one NULL in a unique constraint/index.
So, you'll have to use a trigger to detect duplicates.
It's been requested from MS to ignore NULLS, but SQL 2008 has filtered indexes (as mentioned while I type this)