Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make NOT NULL constraint apply only to new rows

I will try to present the situation as a minimal example:

Say we have a table of tickets, defined as follows:

CREATE TABLE ticket_id_list (
   ticket_id  NUMBER,
   issued_to  VARCHAR2(100),
   CONSTRAINT ticket_id_list_pk PRIMARY KEY (ticket_id)
);

The system is used for some time and data is added to the table:

INSERT INTO ticket_id_list (ticket_id, issued_to)
   VALUES (1, 'Arthur');
INSERT INTO ticket_id_list (ticket_id, issued_to)
   VALUES (2, 'Ford');

Later, following requirement pops up:

We need to store a reference number to some other thing in the ticket table this point onwards. The reference needs to be non null. But old records must have NULL values.

(Stupid as though this sound, this is a real requirement.)

Now if we do this:

ALTER TABLE ticket_id_list
   ADD ref_no VARCHAR2(6) NOT NULL;

the constraint will be violated immediately and we will get: ORA-01758: table must be empty to add mandatory (NOT NULL) column.

We can of course add a check in the business logic, but that is cumbersome. And because of unexplainable reasons, we cannot use a default value. Is there a way to add a NOT NULL contraint that is only applicable to new records?

like image 938
sampathsris Avatar asked Aug 01 '14 05:08

sampathsris


People also ask

Can we add not null constraint existing table?

It is possible to add a NOT NULL constraint to an existing table by using the ALTER TABLE statement. In this case, the column_name must not contain any NULL value before applying the NOT NULL constraint.

How do you add not null constraint to multiple columns?

1. "I want to have a not null constraint on all 3 name columns" - simply run "alter table employee modify (<column name> not null)" for each column. 2. Instead of using "NVL(FIRST_NAME,NVL(MIDDLE_NAME,LAST_NAME))", you could just do "COALESCE(FIRST_NAME,MIDDLE_NAME,LAST_NAME)".

How do you make a constraint not null?

By default, a column can hold NULL values. The NOT NULL constraint enforces a column to NOT accept NULL values. This enforces a field to always contain a value, which means that you cannot insert a new record, or update a record without adding a value to this field.


3 Answers

You could add a check constraint where values after a certain date cannot be null; something like:

alter table ticket_id_list
  add constraint nul_ref
  check (ticket_id < 123456 or ref is not null);

This does presume that ticket_id's only go up over time, if you happen to have a date field in your table it maybe clearer to use that.

like image 100
Emu Avatar answered Oct 06 '22 07:10

Emu


I found a neat trick: NOVALIDATE.

Add a nullable column:

ALTER TABLE ticket_id_list
   ADD ref_no VARCHAR2(6);

And then add a table contraint, but make sure to specify NOVALIDATE:

ALTER TABLE ticket_id_list
   ADD CONSTRAINT new_ref_nonull CHECK(ref_no IS NOT NULL) NOVALIDATE;

Now let's check it:

INSERT INTO ticket_id_list (ticket_id, issued_to, ref_no)
   VALUES (3, 'Zaphod', '2A4252');
1 row inserted

INSERT INTO ticket_id_list (ticket_id, issued_to, ref_no)
   VALUES (4, 'Marvin', NULL);
ORA-02290: check constraint (NEW_REF_NONULL) violated

Just as expected.

Edit: NOVALIDATE will work only if you need not to update the old rows. If you updated the old rows the constraint will fail. For this instance though, this is not an issue.

like image 24
sampathsris Avatar answered Oct 06 '22 07:10

sampathsris


You will have to consider this requirement as a business logic since in you case it is.

Then implement this business logic in a trigger.

CREATE TRIGGER t_ticket_id_list
    BEFORE INSERT ON ticket_id_list
    FOR EACH ROW
      BEGIN
         IF (:NEW is null) THEN
            RAISE_APPLICATION_ERROR(-20001, 'Cannot Insert 1 in this table');
         END IF;
      END;

Note - Same can be done for SQL Server as well MSDN Create trigger documentations

like image 28
Akalanka Avatar answered Oct 06 '22 07:10

Akalanka