Retain Original Value/State of "GENERATED AS IDENTITY" Column - oracle

I have a table that has a unique increment column created using command "GENERATED AS IDENTITY" and the data type is NUMBER(20,0).
Now, the dev want to change the data type to NUMBER(19,0). Since there are hundreds of tables and millions of data, creating a new set of tables just for one column change is not ideal.
So, I manage to create a workflow as follows:
ALTER TABLE my_schema.my_table
ADD REC_ID_TEMP NUMBER(19,0);
UPDATE my_schema.my_table
SET REC_ID_TEMP = REC_ID;
ALTER TABLE my_schema.my_table
DROP COLUMN REC_ID;
ALTER TABLE my_schema.my_table
ADD REC_ID NUMBER(19,0) GENERATED BY DEFAULT AS IDENTITY;
UPDATE my_schema.my_table
SET REC_ID = REC_ID_TEMP;
ALTER TABLE my_schema.my_table
MODIFY REC_ID NUMBER(19,0) GENERATED AS IDENTITY MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1515 //Must start with the last REC_ID value +1// CACHE 20 NOORDER NOCYCLE NOKEEP NOSCALE NOT NULL ENABLE;
This is the best way possible that I can think of with this peanut size brain of mine.
This way, I can make sure that the REC_ID is the exact same as before I change the data type.
But, the problem is, if you read my the last line of my script, I have to check the last REC_ID for the hundreds of table before I can run the last script. I need help to figure out a way that I can retain the original state.

You can use the START WITH LIMIT VALUE clause instead of specifying a number. From the documentation:
START WITH LIMIT VALUE, which is specific to identity_options, can only be used with ALTER TABLE MODIFY. If you specify START WITH LIMIT VALUE, then Oracle Database locks the table and finds the maximum identity column value in the table (for increasing sequences) or the minimum identity column value (for decreasing sequences) and assigns the value as the sequence generator's high water mark. The next value returned by the sequence generator will be the high water mark + INCREMENT BY integer for increasing sequences, or the high water mark - INCREMENT BY integer for decreasing sequences.

A little automation can make this as easily as calling a procedure per table, eg
SQL>
SQL> create table t1 as select * from scott.emp;
Table created.
SQL> create table t2 as select * from dba_objects;
Table created.
SQL> create table t3 as select * from dba_procedures;
Table created.
SQL>
SQL> alter table t1 add rec_id number(20) generated by default as identity start with 100000;
Table altered.
SQL> alter table t2 add rec_id number(20) generated by default as identity start with 100000;
Table altered.
SQL> alter table t3 add rec_id number(20) generated by default as identity start with 100000;
Table altered.
SQL>
SQL> update t1 set rec_id = rownum;
14 rows updated.
SQL> update t2 set rec_id = rownum;
81264 rows updated.
SQL> update t3 set rec_id = rownum;
37644 rows updated.
SQL>
SQL> alter table t1 add primary key ( rec_id);
Table altered.
SQL> alter table t2 add primary key ( rec_id);
Table altered.
SQL> alter table t3 add primary key ( rec_id);
Table altered.
SQL>
SQL> alter table t1 modify rec_id number(20) generated always as identity start with 100000;
Table altered.
SQL> alter table t2 modify rec_id number(20) generated always as identity start with 100000;
Table altered.
SQL> alter table t3 modify rec_id number(20) generated always as identity start with 100000;
Table altered.
SQL>
SQL> create or replace
2 procedure fix_up_my_recid(p_table varchar2) is
3 begin
4 execute immediate 'alter table '||p_table||' add tmp$rec_id number(19,0)';
5
6 execute immediate 'update '||p_table||' set tmp$rec_id = rec_id';
7
8 execute immediate 'alter table '||p_table||' set unused column rec_id';
9
10 execute immediate 'alter table '||p_table||' add rec_id number(19,0) generated by default as identity';
11
12 execute immediate 'update '||p_table||' set rec_id = tmp$rec_id';
13
14 execute immediate 'alter table '||p_table||' set unused column tmp$rec_id';
15
16 execute immediate 'alter table '||p_table||' move online';
17
18 execute immediate 'alter table '||p_table||' modify rec_id number(19,0) generated always as identity start with limit value';
19
20 end;
21 /
Procedure created.
SQL> sho err
No errors.
SQL>
SQL>
SQL>
SQL> desc t1
Name Null? Type
----------------------------------------------------------------------- -------- ------------------------------------------------
EMPNO NOT NULL NUMBER(4)
ENAME VARCHAR2(10)
JOB VARCHAR2(9)
MGR NUMBER(4)
HIREDATE DATE
SAL NUMBER(7,2)
COMM NUMBER(7,2)
DEPTNO NUMBER(2)
REC_ID NOT NULL NUMBER(20)
SQL> exec fix_up_my_recid('T1')
PL/SQL procedure successfully completed.
SQL> exec fix_up_my_recid('T2')
PL/SQL procedure successfully completed.
SQL> exec fix_up_my_recid('T3')
PL/SQL procedure successfully completed.
SQL> desc t1
Name Null? Type
----------------------------------------------------------------------- -------- ------------------------------------------------
EMPNO NOT NULL NUMBER(4)
ENAME VARCHAR2(10)
JOB VARCHAR2(9)
MGR NUMBER(4)
HIREDATE DATE
SAL NUMBER(7,2)
COMM NUMBER(7,2)
DEPTNO NUMBER(2)
REC_ID NOT NULL NUMBER(19)
SQL>
SQL>
SQL>
SQL>
SQL> desc t2
Name Null? Type
----------------------------------------------------------------------- -------- ------------------------------------------------
OWNER VARCHAR2(128)
OBJECT_NAME VARCHAR2(128)
SUBOBJECT_NAME VARCHAR2(128)
OBJECT_ID NUMBER
DATA_OBJECT_ID NUMBER
OBJECT_TYPE VARCHAR2(23)
CREATED DATE
LAST_DDL_TIME DATE
TIMESTAMP VARCHAR2(19)
STATUS VARCHAR2(7)
TEMPORARY VARCHAR2(1)
GENERATED VARCHAR2(1)
SECONDARY VARCHAR2(1)
NAMESPACE NUMBER
EDITION_NAME VARCHAR2(128)
SHARING VARCHAR2(18)
EDITIONABLE VARCHAR2(1)
ORACLE_MAINTAINED VARCHAR2(1)
APPLICATION VARCHAR2(1)
DEFAULT_COLLATION VARCHAR2(100)
DUPLICATED VARCHAR2(1)
SHARDED VARCHAR2(1)
CREATED_APPID NUMBER
CREATED_VSNID NUMBER
MODIFIED_APPID NUMBER
MODIFIED_VSNID NUMBER
REC_ID NOT NULL NUMBER(19)
SQL> desc t3
Name Null? Type
----------------------------------------------------------------------- -------- ------------------------------------------------
OWNER VARCHAR2(128)
OBJECT_NAME VARCHAR2(128)
PROCEDURE_NAME VARCHAR2(128)
OBJECT_ID NUMBER
SUBPROGRAM_ID NUMBER
OVERLOAD VARCHAR2(40)
OBJECT_TYPE VARCHAR2(13)
AGGREGATE VARCHAR2(3)
PIPELINED VARCHAR2(3)
IMPLTYPEOWNER VARCHAR2(128)
IMPLTYPENAME VARCHAR2(128)
PARALLEL VARCHAR2(3)
INTERFACE VARCHAR2(3)
DETERMINISTIC VARCHAR2(3)
AUTHID VARCHAR2(12)
RESULT_CACHE VARCHAR2(3)
ORIGIN_CON_ID NUMBER
POLYMORPHIC VARCHAR2(5)
REC_ID NOT NULL NUMBER(19)
SQL>
Note that I've not used DROP COLUMN because that's incredibly expensive. Generally better to just go with SET UNUSED, and I've thrown in an MOVE ONLINE at the end because all those updates could make a mess of your table row structure.

Related

SQL Query alter table auto increment in SQL Plus Oracle 10g

I have this SQL Query and I am trying to modify my column so it has an auto increment Property, But when I execute the query I either have a problem code ORA-00933 SQL command not properly ended
This is what I have tried
ALTER TABLE BF_USER DROP COLUMN USER_ID
ALTER TABLE BF_USER ADD USER_ID INT IDENTITY(1,1);
SQL*Plus is Oracle's command-line tool. In that case:
SQL> create table bf_user (user_id number, name varchar2(10));
Table created.
SQL> alter table bf_user drop column user_id;
Table altered.
SQL> alter table bf_user add user_id number generated always as identity;
Table altered.
Testing:
SQL> insert into bf_user(name) values ('Littlefoot');
1 row created.
SQL> select * from bf_user;
NAME USER_ID
---------- ----------
Littlefoot 1
SQL>
As you use Oracle 10g (which doesn't support identity columns), use combination of a sequence and a database trigger:
SQL> create table bf_user (user_id number, name varchar2(10));
Table created.
SQL> create sequence bf_seq;
Sequence created.
SQL> create or replace trigger trg_bi_bfu
2 before insert on bf_user
3 for each row
4 begin
5 select bf_seq.nextval into :new.user_id from dual;
6 end;
7 /
Trigger created.
SQL> insert into bf_user (name) values ('Nayeon');
1 row created.
SQL> select * from bf_user;
USER_ID NAME
---------- ----------
1 Nayeon
SQL>

Need to insert machine name and DML command in table through trigger

I need to create a trigger that can insert in an audit table which DML command- insert update delete has been used on a base table plus with machine name.
Please guide me with this.
Let me show you an example:
1.A table my_test where I will make the dml operations
2.An audit table to record the operations
3.A Trigger to capture the values interested.
SQL> create table my_test ( c1 number, c2 number );
Table created.
SQL> create table audit_my_test ( id_timestamp timestamp,
session_id number,
username varchar2(30),
machine varchar2(50),
sql_text varchar2(400),
operation_type varchar2(20)
);
Table created.
SQL> create or replace trigger my_trg_my_test after insert or delete or update on my_test
referencing new as new old as old
for each row
declare
v_sql varchar2(400);
v_usr varchar2(40);
v_ope varchar2(20);
v_ter varchar2(50);
v_sid number;
begin
select sys_context('USERENV','SESSION_USER'),
sys_context('USERENV','CURRENT_SQL'),
sys_context('USERENV','SID'),
sys_context('USERENV','HOST')
into
v_usr,
v_sql,
v_sid,
v_ter
from dual;
IF INSERTING THEN
v_ope := 'Insert';
ELSIF UPDATING THEN
v_ope := 'Update';
ELSIF DELETING THEN
v_ope := 'Delete';
END IF;
insert into audit_my_test values ( systimestamp , v_sid, v_usr, v_ter, v_sql, v_ope );
end;
/
Trigger created.
SQL> show err
No errors
Now let's make some DML operations over the table
SQL> insert into my_test values ( 1 , 1) ;
1 row created.
SQL> insert into my_test values ( 2 , 2) ;
1 row created.
SQL> commit;
Commit complete.
Verify the audit table
ID_TIMESTAMP SESSION_ID USERNAME MACHINE
--------------------------------------------------------------------------- ---------- ------------------------------ ------------------------------
SQL_TEXT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
OPERATION_
----------
24-JUL-20 01.01.25.567641 PM 328 SYS scglvdoracd0006.scger.dev.corp
Insert
24-JUL-20 01.01.45.514662 PM 328 SYS scglvdoracd0006.scger.dev.corp
Insert
ID_TIMESTAMP SESSION_ID USERNAME MACHINE
--------------------------------------------------------------------------- ---------- ------------------------------ ------------------------------
SQL_TEXT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
OPERATION_
----------
You can read all the attributes from sys_context here:
https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions165.htm
The problem with this solution: You will never get the SQL responsible for the operation. CURRENT_SQL in SYS_CONTEXT only works inside a procedure and when it is invoked in a Fine Grain Access (FGA) policy as a handler.
If you need the SQL responsible for the operation, you either construct a FGA policy and a handler, or you use AUDIT which is much better in this specific case.

Alter table clg ADD CONSTRAINT UC_Person NOT NULL(AGE); why do I get an error?

This throws an error:
ALTER TABLE TABLE_NAME
ADD CONSTRAINT UC_Person NOT NULL(AGE);
This works fine:
ALTER TABLE TABLE_NAME
MODIFY Column_name data_type not null;
Question is that 1st one why getting the error and for unique will not get an error.
Why? Because you can't just make up syntax the way you'd want it to be - it is here so that you'd follow it.
If you want to name the NOT NULL constraint, then do it as follows:
SQL> create table table_name (age number);
Table created.
SQL> alter table table_name modify age constraint uc_person not null;
Table altered.
SQL>
It gets simpler if you don't care about constraint name:
SQL> drop table table_name;
Table dropped.
SQL> create table table_name (age number);
Table created.
SQL> alter table table_name modify age not null;
Table altered.
SQL>

Can adding FK or PK constraints invalidate something?

I have a couple of tables with primary-foreign key relations but those constraints do not actually exist. Now I want to add them with an alter table statement.
Will those commands cause any object that depends on the tables to become invalid?
Thanks.
This is a good question. Let's poke the database and see. Here's the set up:
SQL> create table p23 (id number not null, col1 varchar2(10));
Table created.
SQL> create table c23 (id number not null, p_id number not null, col1 varchar2(10));
Table created.
SQL> create or replace procedure tst23
2 is
3 begin
4 insert into p23 values (1, 'ABC');
5 insert into c23 values (11, 1, 'DEF');
6 end;
7 /
Procedure created.
SQL> select status from user_objects where object_name = 'TST23';
STATUS
-------
VALID
SQL>
Everything is copacetic. Now we'll add some constraints.
SQL> alter table p23 add constraint p23_pk primary key (id);
Table altered.
SQL> select status from user_objects where object_name = 'TST23';
STATUS
-------
VALID
SQL> alter table c23 add constraint c23_p23_fk
2 foreign key (p_id) references p23;
Table altered.
SQL> select status from user_objects where object_name = 'TST23';
STATUS
-------
VALID
SQL>
All is still cool. But when we change a table's structure this happens...
SQL> alter table p23 add col2 date;
Table altered.
SQL> select status from user_objects where object_name = 'TST23';
STATUS
-------
INVALID
SQL>
... which is exactly what we would want.
Note that I ran these tests on 11gR2. Oracle introduced fine-grained dependency tracking in 11g, which made programmatic objects more robust when we run DDL on their dependencies. Find out more. So the outcome might be different in earlier versions. It pays to test.
" what is the reason for the procedure to become invalid?"
Structural changes such as adding, modifying or dropping a column potentially have an impact on referencing objects. The procedure had insert statements which didn't specify target columns. So adding a column introduced an ORA-00947: not enough values bug.
But suppose we employed good practice and specified columns?
SQL> create or replace procedure tst23
2 is
3 begin
4 insert into p23 (id, col1) values (1, 'ABC');
5 end;
6 /
Procedure created.
SQL> select status from user_objects where object_name = 'TST23';
STATUS
-------
VALID
SQL> alter table p23 add col4 number;
Table altered.
SQL> select status from user_objects where object_name = 'TST23';
STATUS
-------
VALID
SQL>
Now we're protected by fine-grained dependency tracking. Well, sort of. We shouldn't substitute dependency tracking for impact analysis:
SQL> alter table p23 add col5 date not null;
Table altered.
SQL> select status from user_objects where object_name = 'TST23';
STATUS
-------
VALID
SQL>
The procedure has a VALID state but it will still fail when we run it, because it doesn't populate the new mandatory column.

How to auto increment the id in oracle?

Is there any way to auto increment the id in oracle?
UPDATE: It's working but the sequence start in 2 instead of 1. I already set the sequence start with 1
If it possible not use the sequence but still id's will be auto increment?
Here:
CREATE TABLE tblname
(
fieldname_id number(25),
contract_number number(12) not null,
CONSTRAINT letter_status_pk PRIMARY KEY (fieldname_id, contract_number)
);
Sequence w/trigger:
create sequence fieldname_id_sequence start with 1
increment by 1
minvalue 1;
create trigger tr_tblname
before insert on tblname
for each row
begin
select fieldname_id_sequence.nextval into :NEW.fieldname_id from dual;
end;
insert data:
insert all
into tblname(contract_number,fieldname1,fieldname2,fieldname3,fieldname4,fieldname5,fieldname6,fieldname7,fieldname8,fieldname8,fieldname10,fieldname11) values(3300026224,'values','values','values','3/12/2014','values','values','3/18/2014','3/7/2014','values','values')
into tblname(contract_number,fieldname1,fieldname2,fieldname3,fieldname4,fieldname5,fieldname6,fieldname7,fieldname8,fieldname8,fieldname10,fieldname11) values (3300016335,'values','values','values','3/12/2014','values','values','3/18/2014','3/7/2014','values','values')
select 1 from dual;
you can model it easily with a sequence and a trigger:
Create sequence sequence_name start with value increment by value minvalue value maxvalue value;
First, let’s create an emp table with primary key constraint on emp_id column.
SQL> create table emp ( emp_id number(10),
fname varchar2(25),
lname varchar2(25),
constraint pk_emp_id PRIMARY KEY(emp_id)
);
Now let’s create a sequence.
SQL> Create sequence emp_sequence start with 1
increment by 1
minvalue 1
maxvalue 10000;
Now we have created a sequence object named emp_sequence with starting value as 1 and incrementing by 1 from 1 (minvalue) to 10000 (maxvalue)
SQL> insert into emp (emp_id,fname,lname) values(emp_sequence.nextval,'Darvin','Johnson'); SQL>
insert into emp (emp_id,fname,lname) values(emp_sequence.nextval,'Mig','Andrews');
SQL> insert into emp (emp_id,fname,lname) values(emp_sequence.nextval,'Alex','Martin');
SQL> insert into emp (emp_id,fname,lname) values(emp_sequence.nextval,'Jon','paul');
SQL> insert into emp (emp_id,fname,lname) values(emp_sequence.nextval,'Yatin','Bones');
In emp_sequence.nextval where emp_sequence is the name of sequence we created above and nextval is a function that is used to assign the next number from emp_sequence to emp_id column in emp table.
Oracle 12c introduces IDENTITY COLUMNS.
SQL> CREATE TABLE new_identity_table
2 (
3 ID NUMBER GENERATED ALWAYS AS IDENTITY,
4 text VARCHAR2(50)
5 );
Table created.
SQL>
SQL> INSERT
2 INTO new_identity_table
3 (
4 text
5 )
6 VALUES
7 (
8 'This table has an identity column'
9 );
1 row created.
SQL> column text format A40;
SQL>
SQL> select * from new_identity_table;
ID TEXT
---------- ----------------------------------------
1 This table has an identity column
SQL>
Oracle creates a sequence to populate the identity column. You can find it named as ISEQ$$
SQL> select sequence_name, min_value, max_value, increment_by from user_sequences;
SEQUENCE_NAME MIN_VALUE MAX_VALUE INCREMENT_BY
-------------------- ---------- ---------------------------- ------------
ISEQ$$_93199 1 9999999999999999999999999999 1
SQL>
More more information about the identity columns, use the ALL_TAB_IDENTITY_COLS view.
SQL> SELECT table_name,
2 column_name,
3 generation_type,
4 identity_options
5 FROM all_tab_identity_cols
6 WHERE owner = 'LALIT'
7 ORDER BY 1, 2;
TABLE_NAME COLUMN_NAME GENERATION IDENTITY_OPTIONS
-------------------- --------------- ---------- --------------------------------------------------
NEW_IDENTITY_TABLE ID ALWAYS START WITH: 1, INCREMENT BY: 1, MAX_VALUE: 9999999
999999999999999999999, MIN_VALUE: 1, CYCLE_FLAG: N
, CACHE_SIZE: 20, ORDER_FLAG: N
SQL>

Resources