Alter table to modify default value of column - sql

I have a requirement where we need to modify a column's default value in database table. The table is already an existing table in database and currently the default value of the column is NULL.
Now if add a new default value to this column, If I am correct it updates all the existing NULLs of the column to new DEfault value. Is there a way to not to do this but still set a new default value on column.
I mean I do not want the existing NULLs to be updated and want them to remain as NULLs.
Any help on this is appreciated.
Thanks

Your belief about what will happen is not correct. Setting a default value for a column will not affect the existing data in the table.
I create a table with a column col2 that has no default value
SQL> create table foo(
2 col1 number primary key,
3 col2 varchar2(10)
4 );
Table created.
SQL> insert into foo( col1 ) values (1);
1 row created.
SQL> insert into foo( col1 ) values (2);
1 row created.
SQL> insert into foo( col1 ) values (3);
1 row created.
SQL> select * from foo;
COL1 COL2
---------- ----------
1
2
3
If I then alter the table to set a default value, nothing about the existing rows will change
SQL> alter table foo
2 modify( col2 varchar2(10) default 'foo' );
Table altered.
SQL> select * from foo;
COL1 COL2
---------- ----------
1
2
3
SQL> insert into foo( col1 ) values (4);
1 row created.
SQL> select * from foo;
COL1 COL2
---------- ----------
1
2
3
4 foo
Even if I subsequently change the default again, there will still be no change to the existing rows
SQL> alter table foo
2 modify( col2 varchar2(10) default 'bar' );
Table altered.
SQL> select * from foo;
COL1 COL2
---------- ----------
1
2
3
4 foo
SQL> insert into foo( col1 ) values (5);
1 row created.
SQL> select * from foo;
COL1 COL2
---------- ----------
1
2
3
4 foo
5 bar

ALTER TABLE *table_name*
MODIFY *column_name* DEFAULT *value*;
worked in Oracle
e.g:
ALTER TABLE MY_TABLE
MODIFY MY_COLUMN DEFAULT 1;

ALTER TABLE {TABLE NAME}
ALTER COLUMN {COLUMN NAME} SET DEFAULT '{DEFAULT VALUES}'
example :
ALTER TABLE RESULT
ALTER COLUMN STATUS SET DEFAULT 'FAIL'

Following Justin's example, the command below works in Postgres:
alter table foo alter column col2 set default 'bar';

ALTER TABLE <table_name> MODIFY <column_name> DEFAULT <defult_value>
EX: ALTER TABLE AAA MODIFY ID DEFAULT AAA_SEQUENCE.nextval
Tested on Oracle Database 12c Enterprise Edition Release 12.2.0.1.0

For Sql Azure the following query works :
ALTER TABLE [TableName] ADD DEFAULT 'DefaultValue' FOR ColumnName
GO

Related

Oracle - Adding NOT NULL constraint to a currently nullable column

I have a handful of numeric fields that are currently nullable. I want to add a NOT NULL constraint to these fields and have the default value now set to 0.
Data gets pushed in to this table on a schedule. There is nothing being pushed into these fields, and they're nullable, so these certain fields are all being set to null. This is causing some trouble when we're trying to do math with these fields.
If I first run updates on these fields to set all of the current nulls to zeros, should I have any issues adding a NOT NULL with default 0 constraint on these currently nullable fields? Is there anything I should look out for?
Thanks
edit:
SET SERVEROUTPUT ON
DECLARE
P_PROJECT_NUM VARCHAR2(200);
P_FISCAL_YEAR VARCHAR2(200);
P_RESP_CENTER VARCHAR2(200);
FIELD1 INTEGER;
FIELD2 INTEGER;
FIELD3 INTEGER;
BEGIN
P_PROJECT_NUM := '123456';
P_FISCAL_YEAR := '2019_2020';
P_RESP_CENTER := '123A';
FIELD1 := 0;
FIELD2 := 0;
FIELD3 := 0;
FMS_EXTRACT.LOAD_TEMPLATE(
P_PROJECT_NUM => P_PROJECT_NUM,
P_FISCAL_YEAR => P_FISCAL_YEAR,
P_RESP_CENTER => P_RESP_CENTER,
FIELD1 => FIELD1,
FIELD2 => FIELD2,
FIELD3 => FIELD3
);
COMMIT;
You can do it like this
Add the default clause to the columns
Update the null values to 0
Add the constraint NOT NULL
My example:
SQL> create table t ( c1 number , c2 number );
Table created.
SQL> insert into t values ( 1 , null ) ;
1 row created.
SQL> insert into t values ( null , 1 ) ;
1 row created.
SQL> select * from t ;
C1 C2
---------- ----------
1
1
SQL> alter table t modify c1 number default 0 ;
Table altered.
SQL> alter table t modify c2 number default 0 ;
Table altered.
SQL> update t set c1 = 0 where c1 is null ;
1 row updated.
SQL> update t set c2 = 0 where c2 is null ;
1 row updated.
SQL> commit ;
Commit complete.
SQL> alter table t modify c1 number not null ;
Table altered.
SQL> alter table t modify c2 number not null ;
Table altered.
SQL> insert into t ( c1 ) values ( 2 ) ;
1 row created.
SQL> select * from t ;
C1 C2
---------- ----------
1 0
0 1
2 0
This is too long for a comment. I don't think you want a NOT NULL constraint. That would result in your inserts failing if any of the NOT NULL columns are NULL.
The simplest solution is to not change the data at all. After all, NULL appears to be a valid value, if that is how the data is coming in. Just use COALESCE() in your logic:
select coalesce(x, 0) + coalesce(y, 0)
You can encompass this logic in a view or in generated columns:
alter table t add x_notnull generated always as (coalesce(x, 0));
If you want to prevent rows coming in with NULL values, then you can filter them out in your data important process. You don't provide details, so I can't make any suggestions.
You can add a default value. For instance:
alter table modify y default 0;
Then subsequent inserts where the value is missing will be replaced by the default value. However, explicit NULL values will not be affected.
Finally, you can add a NOT NULL constraint -- after removing all current NULL values.
Here is a db<>fiddle showing some examples of the above.

Fetching a column value for current row in before insert Oracle trigger

I am creating a trigger in oracle which gets triggered before any insert statement. I want to insert a value in column2 using that trigger, but the problem is, I have to first get the the value of another column which is column 1 , of the new row being inserted and based on that value ,I will write business logic to insert the value in column2.
I am unable to access the value of column1 for the new row which will be inserted. How can I access the values of the new row being inserted.
Scenario is like this:
Value for column2 has to be inserted using trigger.
But, to popluate value in column2 , first value for column1 from the row which is going to be inserted, is required.
and Based on the value of column1, value for column2 will be calculated and inserted.
Please help me with syntax and proper resolution.
Use Case is:
Suppose column1 value is TS-1
then column2 value has to be TS-1-1, for the next time column2 value will be
TS-1-2 and so on. SO there could be multiple values for TS-1 in the table, and for that every time vales for column2 will increase as TS-1-,TS-1-2, TS-1-3 and so on, last digit gets incremented for column2.
So I will have the fetch the maximum value of last number getting incremented and increase it by 1 for each insert.
Please help
Once you create such a trigger as below :
SQL> create or replace trigger trg_tab_bi
before insert on tab
for each row
declare
nr int;
begin
select count(1)+1
into nr
from tab
where col1 = :new.col1;
:new.col2 := :new.col1||'-'||nr;
end;
you may manage your desired duty like in a below way:
SQL> insert into tab(col1) values('TS-1');
1 row inserted
SQL> select t.*
2 from tab t
3 order by col1, col2;
COL1 COL2
------ ------
TS-1 TS-1-1
SQL> insert into tab(col1) values('TS-1');
1 row inserted
SQL> select t.*
2 from tab t
3 order by col1, col2;
COL1 COL2
------ ------
TS-1 TS-1-1
TS-1 TS-1-2
SQL> insert into tab(col1) values('TS-1');
1 row inserted
SQL> select t.*
2 from tab t
3 order by col1, col2;
COL1 COL2
------ ------
TS-1 TS-1-1
TS-1 TS-1-2
TS-1 TS-1-3
SQL> insert into tab(col1) values('TS-7');
1 row inserted
SQL> select t.*
2 from tab t
3 order by col1, col2;
COL1 COL2
------ ------
TS-1 TS-1-1
TS-1 TS-1-2
TS-1 TS-1-3
TS-7 TS-7-1
SQL> insert into tab(col1) values('TS-7');
1 row inserted
SQL> select t.*
2 from tab t
3 order by col1, col2;
COL1 COL2
------ ------
TS-1 TS-1-1
TS-1 TS-1-2
TS-1 TS-1-3
TS-7 TS-7-1
TS-7 TS-7-2
You can use these two terms in a trigger :old to reference the old value and :new to reference the new value.
Here is an example from the Oracle documentation linked to above
CREATE OR REPLACE TRIGGER Print_salary_changes
BEFORE DELETE OR INSERT OR UPDATE ON Emp_tab
FOR EACH ROW WHEN (new.Empno > 0)
DECLARE
sal_diff number;
BEGIN
sal_diff := :new.sal - :old.sal;
dbms_output.put('Old salary: ' || :old.sal);
dbms_output.put(' New salary: ' || :new.sal);
dbms_output.put_line(' Difference ' || sal_diff);
END;
In this example the trigger fires BEFORE DELETE OR INSERT OR UPDATE :old.sal will contain the salary prior to the trigger firing and :new.sal will contain the new value.

Multiple insert SQL oracle

How do you do multiple insert with SQL in Oracle 12c when you have an identity column?
INSERT ALL
INTO Table1 (Column2) Values (1)
INTO Table1 (Column2) Values (2)
SELECT * FROM dual;
where Table1 has column1 as an identity, will set the identity column to have the same value which violates the primary key constraint.
CREATE TABLE Table1 (
Table1Id NUMBER GENERATED ALWAYS AS IDENTITY,
column2 VARCHAR2(255),
column3 NUMBER,
PRIMARY KEY (Table1Id)
);
INSERT ALL
INTO Table1 (column2, column3) VALUES ('a', '1')
INTO Table1 (column2, column3) VALUES ('b', '2')
SELECT * FROM dual;
--SQL Error: ORA-00001: unique constraint violated
What am I doing wrong with this?
EDIT Added two test cases, and a possible workaround.
Though Insert statement and insert all statement are practically the same conventional insert statement. But when it comes to sequences, they work differently.
Test case 1 : Identity columns
SQL> DROP TABLE table1 PURGE;
Table dropped.
SQL>
SQL> CREATE TABLE Table1 (
2 Table1Id NUMBER GENERATED ALWAYS AS IDENTITY,
3 column3 NUMBER,
4 PRIMARY KEY (Table1Id)
5 );
Table created.
SQL>
SQL> INSERT ALL
2 INTO Table1 (column3) VALUES ('1')
3 INTO Table1 (column3) VALUES ('2')
4 SELECT * FROM dual;
INSERT ALL
*
ERROR at line 1:
ORA-00001: unique constraint (LALIT.SYS_C0010439) violated
SQL>
Let's see what's actually happening under the hood -
SQL> CREATE TABLE Table1 (
2 Table1Id NUMBER GENERATED ALWAYS AS IDENTITY,
3 column3 NUMBER,
4 CONSTRAINT A UNIQUE (Table1Id)
5 );
Table created.
SQL> INSERT ALL
2 INTO Table1 (column3) VALUES (1)
3 INTO Table1 (column3) VALUES (2)
4 SELECT * FROM dual;
INSERT ALL
*
ERROR at line 1:
ORA-00001: unique constraint (LALIT.A) violated
SQL> SELECT * FROM table1;
no rows selected
SQL> ALTER TABLE table1
2 DISABLE CONSTRAINT a;
Table altered.
SQL> INSERT ALL
2 INTO Table1 (column3) VALUES (1)
3 INTO Table1 (column3) VALUES (2)
4 SELECT * FROM dual;
2 rows created.
SQL> SELECT * FROM table1;
TABLE1ID COLUMN3
---------- ----------
2 1
2 2
SQL>
So, the sequence progressed to nextval however there was an unique constraint violation the first time we did an Insert All. Next, we disabled the unique constraint, and the subsequent Insert All reveals that the sequence did not progress to nextval, rather it attempted to insert duplicate keys.
Though the issue doesn't occur with a INSERT-INTO-SELECT statement.
SQL> INSERT INTO table1(column3) SELECT LEVEL FROM dual CONNECT BY LEVEL <=5;
5 rows created.
SQL>
SQL> SELECT * FROM table1;
TABLE1ID COLUMN3
---------- ----------
2 1
3 2
4 3
5 4
6 5
SQL>
Surprisingly, as per the metadata, the sequence is supposed to proceed to nextval automatically, however it doesn't happen with an Insert All statement.
SQL> SELECT COLUMN_NAME,
2 IDENTITY_COLUMN,
3 DATA_DEFAULT
4 FROM user_tab_cols
5 WHERE table_name ='TABLE1'
6 AND IDENTITY_COLUMN='YES';
COLUMN_NAME IDENTITY_COLUMN DATA_DEFAULT
--------------- --------------- ------------------------------
TABLE1ID YES "LALIT"."ISEQ$$_94458".nextval
SQL>
Test Case 2 : Using a sequence explicitly
The INSERT ALL would work the same way whether an identity column is used or an explicit sequence is used.
SQL> DROP SEQUENCE s;
Sequence dropped.
SQL>
SQL> CREATE SEQUENCE s;
Sequence created.
SQL>
SQL> DROP TABLE t PURGE;
Table dropped.
SQL>
SQL> CREATE TABLE t (
2 ID NUMBER,
3 text VARCHAR2(50),
4 CONSTRAINT id_pk PRIMARY KEY (ID)
5 );
Table created.
SQL>
SQL> INSERT ALL
2 INTO t VALUES (s.nextval, 'a')
3 INTO t VALUES (s.nextval, 'b')
4 INTO t VALUES (s.nextval, 'c')
5 INTO t VALUES (s.nextval, 'd')
6 SELECT * FROM dual;
INSERT ALL
*
ERROR at line 1:
ORA-00001: unique constraint (LALIT.ID_PK) violated
SQL>
SQL> SELECT * FROM T;
no rows selected
SQL>
SQL> ALTER TABLE t
2 DISABLE CONSTRAINT id_pk;
Table altered.
SQL> INSERT ALL
2 INTO t VALUES (s.nextval, 'a')
3 INTO t VALUES (s.nextval, 'b')
4 INTO t VALUES (s.nextval, 'c')
5 INTO t VALUES (s.nextval, 'd')
6 SELECT * FROM dual;
4 rows created.
SQL> SELECT * FROM T;
ID TEXT
---------- ----------------------------------------
2 a
2 b
2 c
2 d
SQL>
Possible workaround - Using a ROW LEVEL trigger
SQL> CREATE OR REPLACE TRIGGER t_trg
2 BEFORE INSERT ON t
3 FOR EACH ROW
4 WHEN (new.id IS NULL)
5 BEGIN
6 SELECT s.NEXTVAL
7 INTO :new.id
8 FROM dual;
9 END;
10 /
Trigger created.
SQL> truncate table t;
Table truncated.
SQL> INSERT ALL
2 INTO t (text) VALUES ('a')
3 INTO t (text) VALUES ('b')
4 INTO t (text) VALUES ('c')
5 INTO t (text) VALUES ('d')
6 SELECT * FROM dual;
4 rows created.
SQL> SELECT * FROM t;
ID TEXT
---------- -------------------------
3 a
4 b
5 c
6 d
SQL>
Here's a workaround using the UNION ALL method instead of the INSERT ALL method. For some reason the data must be wrapped in a select * from (...) or it will generate the error ORA-01400: cannot insert NULL into ("JHELLER"."TABLE1"."TABLE1ID").
insert into table1(column2, column3)
select *
from
(
select 'a', '1' from dual union all
select 'b', '2' from dual
);

Column alias on table declaration

Is there a way, in Oracle 11gR2 that I could create a single column with two names?
Reason I need this is back compatibility, on situations in which this would work:
create table test1 (col1 varchar2(10), col2 [some ref to col1]);
insert into test1 values ('test_value');
and then
SQL> select col1 from test1;
test_value
SQL> select col2 from test1;
test_value
There seem to be a way of thing so on SQL Server, I am looking for the Oracle equivalent to it.
ideas?
You can create a VIRTUAL COLUMN:
CREATE TABLE test1 (
col1 VARCHAR2(10),
col2 VARCHAR2(10) GENERATED ALWAYS AS (col1 || '')
);
INSERT INTO test1 (col1) VALUES ('test_value');
COMMIT;
SELECT * FROM test1;
COL1 COL2
---------- ----------
test_value test_value
However, virtual columns cannot be manipulated by DML. Read more here: Oracle Base - Virtual Columns

Update with after insert trigger on same table

I have a to write a insert trigger on a tableA. which will perform update with same table but different column. I am getting error while doing this. My trigger is
create or replace trigger trigger_A
after insert on table_A
begin
update table_A set col1=1 where col1 is null;
end;
I have an application will perform col2 alone will be inserted and col1 will be kept null. so my trigger will give value for col1 once the row is inserted. But i am getting error saying "Trigger is failed and invalid" when a row is inserted.
How to do this. TIA.
If you want to assign a simple default value, the easiest way is to declare it on the table, using the DEFAULT clause.
SQL> create table t42
2 ( col1 number default 1 not null
3 , col2 date)
4 /
Table created.
SQL> insert into t42 (col2) values (sysdate)
2 /
1 row created.
SQL> select * from t42
2 /
COL1 COL2
---------- ---------
1 03-AUG-11
SQL>
This works with literals or pseudocolumns such as SYSDATE or USER. If you want to derive a more complicated value with a user-defined function or a sequence, you will need to use
a trigger.
Here is a new version of the table...
SQL> create table t42
2 ( col1 number default 1 not null
3 , col2 date default sysdate
4 , col3 varchar2(30) default user
5 , col4 number )
6 /
Table created.
SQL>
... with a trigger:
SQL> create or replace trigger t42_trg
2 before insert or update
3 on t42
4 for each row
5 begin
6 if :new.col4 is null
7 then
8 :new.col4 := my_seq.nextval;
9 end if;
10 end;
11 /
Trigger created.
SQL> insert into t42 (col1, col2, col3)
2 values (99, sysdate, 'MR KNOX')
3 /
1 row created.
SQL> select * from t42
2 /
COL1 COL2 COL3 COL4
---------- --------- ------------------------------ ----------
99 03-AUG-11 MR KNOX 161
SQL>
Note that although every column on the table is defaultable, I have to populate at least one column to make the SQL valid:
SQL> insert into t42 values ()
2 /
insert into t42 values ()
*
ERROR at line 1:
ORA-00936: missing expression
SQL>
But I can pass in NULL to COL4 to get a completely defaulted record:
SQL> insert into t42 (col4) values (null)
2 /
1 row created.
SQL> select * from t42
2 /
COL1 COL2 COL3 COL4
---------- --------- ------------------------------ ----------
99 03-AUG-11 MR KNOX 161
1 03-AUG-11 APC 162
SQL>
Caveat lector: my trigger uses the new 11g syntax. In previous versions we have to assign the sequence value using a SELECT statement:
select my_seq.nextval
into :new.col4
from dual;
You cannot update a table where the trigger is invoked:
Within a stored function or trigger, it is not permitted to modify a
table that is already being used (for reading or writing) by the
statement that invoked the function or trigger.
Doing so will generate Error 1442:
Error Code: 1442
Can't update table 'MyTable' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
In short, we are not allowed to update the table in use - but your case is simple, only want to update the field if it's NULL, for this choose BEFORE INSERT ON trigger, this way you can update all the fields of the new/current entry/row (as it has not been entered yet):
DELIMITER //
DROP TRIGGER IF EXISTS trigger_A//
CREATE TRIGGER trigger_A BEFORE INSERT ON table_A
FOR EACH ROW BEGIN
IF NEW.col1 IS NULL THEN
set NEW.col1 = <some-value>;
ENF IF;
END;
//
DELIMITER ;