Autopopulating a column with a Trigger SQL - sql

I'm trying to start a sequence that automatically populates a column when an insertion has been made. It should start from 500 and increment by 1. Any idea how would I go about doing this? So far I have something like this, but it seems to crash
CREATE TRIGGER ADD_TRIGGER ON tablename
AFTER INSERT
AS
BEGIN
ADD colname NUMBER IDENTITY(500,1);
END
GO

You can create a sequence
CREATE SEQUENCE mySeq
START WITH 500
INCREMENT BY 1
CACHE 100;
and then use it in your trigger
CREATE OR REPLACE TRIGGER myTrigger
AFTER INSERT ON table_name
FOR EACH ROW
BEGIN
SELECT mySeq.nextval
INTO :new.colname
FROM dual;
END;

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
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
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
For pre 12c releases, see Auto-increment primary key in Pre 12c releases (Identity functionality)

Related

Oracle 10g Error: Warning: Trigger created with compilation errors

I have created database where I want autoincrement the primary key. I tried to trigger it but getting the above error
here is my description of table:
SQL> desc users
Name Null? Type
----------------------------------------- -------- ----------------------------
USER_ID NOT NULL NUMBER(8)
FIRST_NAME NOT NULL VARCHAR2(50)
LAST_NAME NOT NULL VARCHAR2(50)
CITY VARCHAR2(20)
COUNTRY VARCHAR2(20)
PASSWORD NOT NULL VARCHAR2(16)
EMAIL_ID NOT NULL VARCHAR2(50)
when I am trying to trigger it getting the error:
CREATE SEQUENCE SYSTEM.MYSEQ
2 START WITH 1
3 MAXVALUE 99999999
4 MINVALUE 1
5 NOCYCLE
6 CACHE 20
7 NOORDER;
CREATE OR REPLACE TRIGGER TR_USERS BEFORE INSERT ON USERS FOR EACH ROW
2 BEGIN SELECT LPAD(LTRIM(RTRIM(TO_CHAR(MYSEQ.NEXTVAL))),10,'0') INTO :NEW.USER_ID FROM DUAL;
3 /
please help me to solve this error.
You are getting the error, because you are missing the trigger's END:
CREATE OR REPLACE TRIGGER TR_USERS
BEFORE INSERT ON USERS
FOR EACH ROW
BEGIN
SELECT LPAD(LTRIM(RTRIM(TO_CHAR(MYSEQ.NEXTVAL))),10,'0')
INTO :NEW.USER_ID
FROM DUAL;
END; -- <=== this one
/
The trigger doesn't seem to make much sense, by the way. LPAD(LTRIM(RTRIM(TO_CHAR(MYSEQ.NEXTVAL))),10,'0') is just an obfuscated TO_CHAR(MYSEQ.NEXTVAL, 'FM0000000000'), but then, why create a string with leading zeros, when USERS.USER_ID is numeric??? You turn 123 into '0000000123' only to store it as 123.
There's nothing wrong with the trigger, as far as compilation is concerned (apart from the fact that you "forgot" to END it).
SQL> CREATE OR REPLACE TRIGGER TR_USERS
2 BEFORE INSERT ON USERS
3 FOR EACH ROW
4 BEGIN
5 SELECT lpad(ltrim(rtrim(to_char(myseq.nextval))), 10, '0')
6 INTO :new.user_id
7 FROM dual;
8 END;
9 /
Trigger created.
SQL> INSERT INTO USERS (FIRST_NAME) VALUES ('Little');
1 row created.
SQL> SELECT * FROM users;
USER_ID FIRST_NAME LAST_NAE CITY COUNTRY PASSW EMAIL_ID
---------- --------------- ---------- ---------- ---------- ----- --------------------
1 Little
SQL>
However, if USER_ID is NUMBER, you're overcomplicated trigger code because whatever you do with those functions, you'll - at the end - get just a number. As you can see from my example, USER_ID = 1.
If it were VARCHAR2, then
SQL> TRUNCATE TABLE users;
Table truncated.
SQL> ALTER TABLE USERS MODIFY user_id VARCHAR2(10);
Table altered.
SQL> INSERT INTO USERS (FIRST_NAME) VALUES ('Foot');
1 row created.
SQL> SELECT * FROM users;
USER_ID FIRST_NAME LAST_NAE CITY COUNTRY PASSW EMAIL_ID
---------- --------------- ---------- ---------- ---------- ----- --------------------
0000000002 Foot
SQL>
See the difference?
Trigger could've been simpler (but not much simpler; you're on 10g, after all) as there's nothing to trim:
SQL> CREATE OR REPLACE TRIGGER TR_USERS
2 BEFORE INSERT ON USERS
3 FOR EACH ROW
4 BEGIN
5 SELECT lpad(to_char(myseq.nextval), 10, '0')
6 INTO :new.user_id
7 FROM dual;
8 END;
9 /
Trigger created.
SQL> INSERT INTO USERS (FIRST_NAME) VALUES ('Krishna');
1 row created.
SQL> SELECT * FROM users;
USER_ID FIRST_NAME LAST_NAE CITY COUNTRY PASSW EMAIL_ID
---------- --------------- ---------- ---------- ---------- ----- --------------------
0000000002 Foot
0000000003 Krishna
SQL>
Apart from the missing end keyword, you don't need all the character formatting or the 'select from dual'. I would just use:
create or replace trigger tr_users
before insert on users
for each row
begin
:new.user_id := myseq.nextval;
end;
By the way, your sequence can also be written more simply as:
create sequence myseq;
Also, there is no need to code in uppercase. It's a bad habit from the 1970s.

is this possible? adding 4 new columns: createdDate,modifiedDate,createdBy,modifiedBy?

I'm on sql developer. I have a table. I Want to add those 4 new columns which I know how to do, but I want those values to not be entered by the user when he enters a new row or edits an existing row, I want those values to be automatically filled
For example if the user enters
insert into tableName values (val1,val2,val3)
then the table will have the 7 new values in the new row:
val1,val2,val3,createdDate,modifiedDate,createdBy,modifiedBy
same when the user modifies a value in an existing row
update TAbleName set val1 = newVal where id = id1
and then the "modifiedDate" and "modifiedBy" fields in that row will be automatically modified
What database do you use?
For auto fill when you add a new row, you need to setup "default binding" aka "default field"
ALTER TABLE YourTable
ADD CONSTRAINT DF_YourTable DEFAULT GETDATE() FOR YourColumn
For update, you need to make a trigger to edit the column
How to: Create trigger for auto update modified date with SQL Server 2008
Partially, column's default value can do that (for created date and user who did that); for modifications, use a trigger.
Here's an example:
SQL> create table test (id number, name varchar2(20));
Table created.
SQL> alter table test add
2 (created_date date default sysdate,
3 created_by varchar2(30) default user,
4 modified_date date,
5 modified_by varchar2(30)
6 );
Table altered.
SQL> insert into test (id, name) values (1, 'Little');
1 row created.
SQL> select * From test;
ID NAME CREATED_DATE CREATED_BY MODIFIED_DATE MODIFIED_B
---------- -------------------- ------------------- ---------- ------------------- ----------
1 Little 13.02.2020 22:23:17 SCOTT
Updating a row a little bit later - nothing has changed (for created and modified columns):
SQL> update test set name = 'Foot' where id = 1;
1 row updated.
SQL> select * From test;
ID NAME CREATED_DATE CREATED_BY MODIFIED_DATE MODIFIED_B
---------- -------------------- ------------------- ---------- ------------------- ----------
1 Foot 13.02.2020 22:23:17 SCOTT
Let's create a trigger. It's a simple one:
SQL> create or replace trigger trg_testmod_bu
2 before update on test
3 for each row
4 begin
5 :new.modified_date := sysdate;
6 :new.modified_by := user;
7 end;
8 /
Trigger created.
SQL> update test set name = 'Bigfoot' where id = 1;
1 row updated.
SQL> select * From test;
ID NAME CREATED_DATE CREATED_BY MODIFIED_DATE MODIFIED_B
---------- -------------------- ------------------- ---------- ------------------- ----------
1 Bigfoot 13.02.2020 22:23:17 SCOTT 13.02.2020 22:26:38 SCOTT
Right; the trigger updated both modified columns.

Returning the value of identity column after insertion in Oracle

How do I return the value of an identity column (id) in Oracle 12c after insertion? Seems like most of the approaches out there uses sequence to get back the id of the inserted item.
Simply use the RETURNING clause.
For example -
RETURNING identity_id INTO variable_id;
Test case -
SQL> set serveroutput on
SQL> CREATE TABLE t
2 (ID NUMBER GENERATED ALWAYS AS IDENTITY, text VARCHAR2(50)
3 );
Table created.
SQL>
SQL> DECLARE
2 var_id NUMBER;
3 BEGIN
4 INSERT INTO t
5 (text
6 ) VALUES
7 ('test'
8 ) RETURNING ID INTO var_id;
9 DBMS_OUTPUT.PUT_LINE('ID returned is = '||var_id);
10 END;
11 /
ID returned is = 1
PL/SQL procedure successfully completed.
SQL>
SQL> select * from t;
ID TEXT
---------- --------------------------------------------
1 test
SQL>

How to handle Oracle Error [ Unique Constraint ] error

I have a table named TABLE_1 which has 3 columns
row_id row_name row_descr
1 check1 checks here
2 check2 checks there
These rows are created through a front end application. Now suppose I delete the entry with row_name check2 from the front end and create another entry from front end with row_name check3, in database my entries will be as follows.
row_id row_name row_descr
1 check1 checks here
3 check3 checks
Now row_id if you observe is not a normal one time increment, Now my problem is i'm writing an insert statement to automate something and i don't know what i should insert in the row_id column. Previously i thought it is just new row_id = old row_id +1. But this is not the case here. Please help
EDIT :
Currently im inserting like this which is Wrong :
insert into TABLE1 (row_id, row_name, row_descr
) values ( (select max (row_id) + 1 from TABLE1),'check1','checks here');
row_id is not a normal one time increment.
Never ever calculate ids by max(id)+1 unless you can absolutly exclude simultaneous actions ( which is almost never ever the case). In oracle (pre version 12 see Kumars answer) create a sequence once and insert the values from that sequences afterwards.
create sequence my_sequence;
Either by a trigger which means you don't have to care about the ids during the insert at all:
CREATE OR REPLACE TRIGGER myTrigger
BEFORE INSERT ON TABLE1 FOR EACH ROW
BEGIN
SELECT my_sequence.NEXTVAL INTO :NEW.row_id FROM DUAL;
END;
/
Or directly with the insert
insert into TABLE1 (row_id, row_name, row_descr
) values ( my_sequence.nextval,'check1','checks here');
Besides using row_id as column name in oracle might be a little confusing, because of the pseudocolumn rowid which has a special meaning.
To anwser your quetstion though: If you really need to catch oracle errors as excpetions you can do this with PRAGMA EXCEPTION INIT by using a procedure for your inserts. It might look somehow like this:
CREATE OR REPLACE PROCEDURE myInsert( [...] )
IS
value_allready_exists EXCEPTION;
PRAGMA EXCEPTION_INIT ( value_allready_exists, -00001 );
--ORA-00001: unique constraint violated
BEGIN
/*
* Do your Insert here
*/
EXCEPTION
WHEN value_allready_exists THEN
/*
* Do what you think is necessary on your ORA-00001 here
*/
END myInsert;
Oracle 12c introduced IDENTITY columns. Precisely, Release 12.1. It is very handy with situations where you need to have a sequence for your primary key column.
For example,
SQL> DROP TABLE identity_tab PURGE;
Table dropped.
SQL>
SQL> CREATE TABLE identity_tab (
2 ID NUMBER GENERATED ALWAYS AS IDENTITY,
3 text VARCHAR2(10)
4 );
Table created.
SQL>
SQL> INSERT INTO identity_tab (text) VALUES ('Text');
1 row created.
SQL> DELETE FROM identity_tab WHERE ID = 1;
1 row deleted.
SQL> INSERT INTO identity_tab (text) VALUES ('Text');
1 row created.
SQL> INSERT INTO identity_tab (text) VALUES ('Text');
1 row created.
SQL> INSERT INTO identity_tab (text) VALUES ('Text');
1 row created.
SQL> DELETE FROM identity_tab WHERE ID = 2;
1 row deleted.
SQL> SELECT * FROM identity_tab;
ID TEXT
---------- ----------
3 Text
4 Text
SQL>
Now let's see what's under the hood -
SQL> SELECT table_name,
2 column_name,
3 generation_type,
4 identity_options
5 FROM all_tab_identity_cols
6 WHERE owner = 'LALIT'
7 /
TABLE_NAME COLUMN_NAME GENERATION IDENTITY_OPTIONS
-------------------- --------------- ---------- --------------------------------------------------
IDENTITY_TAB 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>
So, there you go. A sequence implicitly created by Oracle.
And don't forget, you can get rid off the sequence only with the purge option with table drop.
If you are not worried about which values are causing the error, then you could handle it by including a /*+ hint */ in the insert statement.
Here is an example where we would be selecting from another table, or perhaps an inner query, and inserting the results into a table called TABLE_NAME which has a unique constraint on a column called IDX_COL_NAME.
INSERT /*+ ignore_row_on_dupkey_index(TABLE_NAME(IDX_COL_NAME)) */
INTO TABLE_NAME(
INDEX_COL_NAME
, col_1
, col_2
, col_3
, ...
, col_n)
SELECT
INDEX_COL_NAME
, col_1
, col_2
, col_3
, ...
, col_n);
Oracle will blow past the redundant row. This is not a great solution if you care about know WHICH row is causing the issue, or anything else. But if you don't care about that and are fine just keeping the first value that was inserted, then this should do the job.
You can use an exception build in which will raise whenever there will be duplication on unique key
DECLARE
emp_count number;
BEGIN
select count(*) into emp_count from emp;
if emp_count < 1 then
insert into emp
values(1, 'First', 'CLERK', '7839', SYSDATE, 1200, null, 30);
dbms_output.put_line('Clerk added');
else
dbms_output.put_line('No data added');
end if;
EXCEPTION
when dup_val_on_index then
dbms_output.put_line('Tried to add row with duplicated index');
END;

default value, oracle sp call

I have an oralcle SP forced on me that will not accept an empty parameter in an update. So if I wanted to set a value back to the default of ('') it will not let me pass in the empty string. Is there a keyword you can use such as default, null, etc that oracle would interpret back to the default specified for a particular column?
Sometimes things are just as simple as you hope they might be.
First, a table with a default value ...
SQL> create table t23 (
2 id number not null primary key
3 , col_d date default sysdate not null )
4 /
Table created.
SQL> insert into t23 values (1, trunc(sysdate, 'yyyy'))
2 /
1 row created.
SQL> select * from t23
2 /
ID COL_D
---------- ---------
1 01-JAN-10
SQL>
Next a procedure which updates the default column ...
SQL> create or replace procedure set_t23_date
2 ( p_id in t23.id%type
3 , p_date in t23.col_d%type )
4 is
5 begin
6 update t23
7 set col_d = p_date
8 where id = p_id;
9 end;
10 /
Procedure created.
SQL>
... but which doesn't work as we would like:
SQL> exec set_t23_date ( 1, null )
BEGIN set_t23_date ( 1, null ); END;
*
ERROR at line 1:
ORA-01407: cannot update ("APC"."T23"."COL_D") to NULL
ORA-06512: at "APC.SET_T23_DATE", line 6
ORA-06512: at line 1
SQL>
So, let's try adding a DEFAULT option ...
SQL> create or replace procedure set_t23_date
2 ( p_id in t23.id%type
3 , p_date in t23.col_d%type )
4 is
5 begin
6 if p_date is not null then
7 update t23
8 set col_d = p_date
9 where id = p_id;
10 else
11 update t23
12 set col_d = default
13 where id = p_id;
14 end if;
15 end;
16 /
Procedure created.
SQL>
... and lo!
SQL> exec set_t23_date ( 1, null )
PL/SQL procedure successfully completed.
SQL>
SQL> select * from t23
2 /
ID COL_D
---------- ---------
1 28-FEB-10
SQL>
I ran this example on an 11g database. I can't remember when Oracle introduced this exact support for DEFAULT, but it has been quite a while (9i???)
edit
The comments are really depressing. The entire point of building PL/SQL APIs is to make it easier for application developers to interact with the database. That includes being sensible enough to rewrite stored procedures when necessary. The big difference between building something out of software and, say, welding cast-iron girders together is that software is malleable and easy to change. Especially when the change doesn't alter the signature or behaviour of an existing procedure, which is the case here.
The procedure that's been forced on you:
create or replace procedure notEditable(varchar2 bar) as
begin
--update statement
null;
end;
How to use:
begin
notEditable(bar=>null);
end;
I didn't actually compile, but I believe this is the correct syntax.