Automatic sequence in oracle - sql

I am trying to cretae a automatic sequence number genaration using trigger but it is giving me following wrror while inserting the values.
ALTER TABLE sppinv_tblinventory_ex ADD (
CONSTRAINT sppinv_tblinventory_PK PRIMARY KEY (uniqueid));
create sequence row_seq ;
create or replace trigger row_count before insert on sppinv_tblinventory_ex
for each row
begin
select row_seq.nextval into : new.uniqueid from dual;
end;
if I am excuting below then I am able to insert values
insert into sppinv_tblinventory_ex
select row_seq.nextval,
b.member_id,b.src_claim_nbr,b.client_nbr,b.src_platform_cd,
b.suspense_date,b.batch_gen_key,b.bucket_name,b.grouper_rule,
b.event_number,b.case_stat,b.case_stat_dt,b.assigned_to,
b.assigned_on,b.followup_dt,b.release_ind,b.release_dt,
b.viewtype
from sppinv_tblinventory b
When I am inserting the values with out uniqueID I am getting error like below
insert into sppinv_tblinventory_ex
select b.member_id,b.src_claim_nbr,b.client_nbr,b.src_platform_cd,
b.suspense_date,b.batch_gen_key,b.bucket_name,b.grouper_rule,
b.event_number,b.case_stat,b.case_stat_dt,b.assigned_to,
b.assigned_on,b.followup_dt,b.release_ind,b.release_dt,
b.viewtype
from sppinv_tblinventory b
ORA-00947: not enough values
Note : I dont want to disable the trigger

ORA-00947: not enough values - means you have n number of columns in the table but you are only supplying values for (n-m) number of fields.
In your case, if you dont want to insert unique id, then you may have to do
Insert into sppinv_tblinventory_ex (col1, col2, col3.. coln) select (val1, val2, val3 .. valn)

There are so many answers for this on internet...
Try removing the space before new.uniqueid ; and add a IF test like this :
create or replace trigger row_count
before insert on sppinv_tblinventory_ex
for each row
begin
IF :new.uniqueid IS NULL THEN
select row_seq.nextval into :new.uniqueid from dual;
END IF;
end;
Now if you put null in the corresponding value field in your insert, it should work

Related

SQL - Trigger is invalid and failed re-validation

When a user creates an application for the table 'application' I want a copy placed into the table 'application_history'. I've tried creating a trigger and get the error that is in the title.
create or replace TRIGGER create_history AFTER INSERT ON application
FOR EACH ROW
DECLARE
BEGIN
insert into application_history values (:new.arn, :new.srn, :new.job_id, :new.application_status);
END;
Any help will be much appreciated.
As Justin suspected, you seem to be having more than 4 columns in application_history. Either provide values for all columns or specify columns lie 'insert into table1 (col1, col2) values (1,2)'
It worked for me after adding extra column 'hist_id'(just to give you an idea) ...
create table application (arn number, srn number, job_id number, application_status char(1) );
create table application_history (hist_id number, arn number, srn number, job_id number, application_status char(1) );
create or replace TRIGGER create_application_history AFTER INSERT ON application
FOR EACH ROW
DECLARE
BEGIN
insert into application_history values (1, :new.arn, :new.srn, :new.job_id, :new.application_status);
END;
/
I think you need a / at the end. So:
create or replace TRIGGER create_history AFTER INSERT ON application
FOR EACH ROW
DECLARE
BEGIN
insert into application_history values (:new.arn, :new.srn, :new.job_id, :new.application_status);
END;
/

In oracle procedure how to fetch the PKId just added to be added as FK ID for next table

In an oracle procedure, I need to insert data in EmployeeHeader table and then later insert the PK id of this table as the FK id of EmployeeDetails table. How can we achieve this?
INSERT INTO EmployeeHeader(
HEADER_PK_ID
empNo
)
VALUES(
HEADER_PK_ID_SEQ.NEXTVAL,
'SOMETHING'
);
INSERT INTO EmployeeDetails (
DTLHEADER_PK_ID,
HEADER_fK_ID
empname,
age
)
VALUES(
DTLHEADER_PK_ID_SEQ.NEXTVAL,
HEADER_PK_IDn, -- (THIS NEEDS TO BE FETCHED FROM EmployeeHeader)
21
);
You can use currval in most cases:
select HEADER_PK_ID_SEQ.CURRVAL
from dual;
You might need to wrap the two inserts in a single transaction, if you want the values to be safe for concurrent inserts.
Use the RETURNING clause of the INSERT statement:
DECLARE
nHeader_pk_id NUMBER;
BEGIN
INSERT INTO EmployeeHeader
(HEADER_PK_ID, EMPNO)
VALUES
(HEADER_PK_ID_SEQ.NEXTVAL, 'SOMETHING')
RETURNING HEADER_PK_ID INTO nHeader_pk_id;
INSERT INTO EmployeeDetails
(DTLHEADER_PK_ID, HEADER_FK_ID, EMPNAME, AGE)
VALUES
(DTLHEADER_PK_ID_SEQ.NEXTVAL, nHeader_pk_id, 'Somebody', 21);
END;
My personal preference is to use ON INSERT triggers to handle the population of primary key fields, in the following manner:
CREATE OR REPLACE TRIGGER EMPLOYEEHEADER_BI
BEFORE INSERT ON EMPLOYEEHEADER
FOR EACH ROW
BEGIN
IF :NEW.HEADER_PK_ID IS NULL THEN
:NEW.HEADER_PK_ID := HEADER_PK_ID_SEQ.NEXTVAL;
END IF;
END EMPLOYEEHEADER_BI;
CREATE OR REPLACE TRIGGER EMPLOYEEDETAILS_BI
BEFORE INSERT ON EMPLOYEEDETAILS
FOR EACH ROW
BEGIN
IF :NEW.DTLHEADER_PK_ID IS NULL THEN
:NEW.DTLHEADER_PK_ID := DTLHEADER_PK_ID_SEQ.NEXTVAL;
END IF;
END EMPLOYEEDETAILS_BI;
and the INSERT statements become:
DECLARE
nHeader_pk_id NUMBER;
nDtlheader_pk_id NUMBER;
BEGIN
INSERT INTO EmployeeHeader
(EMPNO) -- Note: PK field not mentioned - will be populated by trigger
VALUES
('SOMETHING')
RETURNING HEADER_PK_ID INTO nHeader_pk_id;
INSERT INTO EmployeeDetails
(HEADER_FK_ID, EMPNAME, AGE) -- Note: PK field not mentioned - will be populated by trigger
VALUES
(nHeader_pk_id, 'Somebody', 21)
RETURNING DTLHEADER_PK_ID INTO nDtlheader_pk_id;
END;
(I use the IF pk_field IS NULL THEN construct because I often need to copy data from production to development databases and wish to preserve any key values pulled from production to simplify debugging. If you don't have this requirement you can eliminated the IS NULL check and just assign the sequence's NEXTVAL directly to the column).
Done in this manner the application code doesn't need to know or care about which sequence is used to generate the primary key value for a particular table, and the primary key field is always going to end up populated.
Best of luck.
You can use the RETURNING clause.
Here's an example:
create sequence test_seq1
start with 1
maxvalue 999
minvalue 1
nocycle
cache 20
noorder;
create sequence test_seq2
start with 100
maxvalue 999
minvalue 1
nocycle
cache 20
noorder;
create table test_tab_p (col1 number, col2 number);
create table test_tab_c (col1 number, col2 number, col3 number);
declare
v_p_col2 number := 1;
v_c_col3 number := 10;
v_p_col1 number;
begin
insert into test_tab_p (col1, col2)
values (test_seq1.nextval, v_p_col2)
returning col1 into v_p_col1;
insert into test_tab_c (col1, col2, col3)
values (test_seq2.nextval, v_p_col1, v_c_col3);
commit;
end;
/
select * from test_tab_p;
COL1 COL2
---------- ----------
1 1
select * from test_tab_c;
COL1 COL2 COL3
---------- ---------- ----------
100 1 10
Use currval
INSERT INTO EmployeeHeader
(header_pk_id, empNo)
VALUES
(header_pk_id_seq.nextval, 'SOMETHING');
INSERT INTO EmployeeDetails
(dtlheader_pk_id, header_fk_id, empname, age)
VALUES
(dtlheader_pk_id_seq.nextval, header_pk_id_seq.currval, 21);
currval is safe to use here. It always returns the last value obtained by nextval for the current connection. So even if other transactions (which means a different connection) calls nextval between those two statements, currval will still reflect the value of "this" nextval call.
Quote from the manual:
Each user that references a sequence has access to his or her current sequence number, which is the last sequence generated in the session. A user can issue a statement to generate a new sequence number or use the current number last generated by the session. After a statement in a session generates a sequence number, it is available only to this session.
The very efficient way will be just save the seq id value into a variable and then use it for the whole transaction. Below is the mentioned example. Let me know for any issues.
DECLARE
lv_pkid PLS_INTEGER:=SEQ.NEXTVAL;
BEGIN
INSERT
INTO EmployeeHeader
(
HEADER_PK_ID,
empNo
)
VALUES
(
lv_pkid,
'SOMETHING'
);
INSERT
INTO EmployeeDetails
(
DTLHEADER_PK_ID,
HEADER_fK_ID,
empname,
age
)
VALUES
(
DTLHEADER_PK_ID_SEQ.NEXTVAL,
lv_pkid, -- (THIS NEEDS TO BE FETCHED FROM EmployeeHeader)
21
);
COMMIT;
END;

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;

INSERT ALL INTO and Sequence.nextval for a Surrogate Key

I'm trying to insert 40 rows using an INSERT ALL INTO and I'm not certain on how to insert the surrogate key. Here's what I have
BEGIN
INSERT ALL
INTO question(question_id)
VALUES (question_seq.nextval)
END
Now if I add another INTO VALUES then I get a unique constraint violation.
BEGIN
INSERT ALL
INTO question(question_id)
VALUES (question_seq.nextval)
INTO question(question_id)
VALUES (question_seq.nextval)
END
How can I update the sequences nextval value for each INTO VALUES so that I can avoid the unique constraint violation? I assumed that nextval would automatically update itself.
UPDATE: I don't know if this is the best way to handle this but here's the solution I came up with:
first I created a function that returns a value
then I called that function in the id field of the VALUES clause
create or replace
FUNCTION GET_QUESTION_ID RETURN NUMBER AS
num NUMBER;
BEGIN
SELECT UHCL_QUESTIONS_SEQ.nextval
INTO num
FROM dual;
return num;
END GET_QUESTION_ID;
INSERT ALL
INTO question(question_id)
VALUES (GET_QUESTION_ID())
INTO question(question_id)
VALUES (GET_QUESTION_ID())
Being from a SQL Server background, I've always created a trigger on the table to basically emulate IDENTITY functionality. Once the trigger is on, the SK is automatically generated from the sequence just like identity and you don't have to worry about it.
You can use something like this:
insert into question(question_id)
select question_seq.nextval from
(
select level from dual connect by level <= 40
);
Although it's not a very convenient format, especially if there are other columns you want to add. You'd probably need to create another UNION ALL query, and join it by the LEVEL or ROWNUM.
My first thought was to do something like this:
insert into question(question_id)
select question_seq.nextval value from dual
union all
select question_seq.nextval from dual;
But it generates ORA-02287: sequence number not allowed here, due to the restrictions on sequence values.
By the way, are you sure your INSERT ALL works without a subquery? I get the error ORA-00928: missing SELECT keyword, and the diagram from the 11.2 manual implies there must be a subquery:
I don't know if this is the best way to handle this but here's the solution I came up with:
first I created a function that returns a value
then I called that function in the id field of the VALUES clause
create or replace
FUNCTION GET_QUESTION_ID RETURN NUMBER AS
num NUMBER;
BEGIN
SELECT UHCL_QUESTIONS_SEQ.nextval
INTO num
FROM dual;
return num;
END GET_QUESTION_ID;
INSERT ALL
INTO question(question_id)
VALUES (GET_QUESTION_ID())
INTO question(question_id)
VALUES (GET_QUESTION_ID())

SQL to insert only if a certain value is NOT already present in the table?

How to insert a number into the table, only if the table does not already have that number in it?
I am looking for specific SQL code, not really sure how to approach this. Tried several things, nothing's working.
EDIT
Table looks like this:
PK ID Value
1 4 500
2 9 3
So if I am trying to INSERT (ID, Value) VALUES (4,100) it should not try to do it!
If ID is supposed to be unique, there should be a unique constraint defined on the table. That will throw an error if you try to insert a value that already exists
ALTER TABLE table_name
ADD CONSTRAINT uk_id UNIQUE( id );
You can catch the error and do whatever you'd like if an attempt is made to insert a duplicate key-- anything from ignoring the error to logging and re-raising the exception to raising a custom exception
BEGIN
INSERT INTO table_name( id, value )
VALUES( 4, 100 );
EXCEPTION
WHEN dup_val_on_index
THEN
<<do something>>
END;
You can also code the INSERT so that it inserts 0 rows (you would still want the unique constraint in place both from a data model standpoint and because it gives the optimizer more information and may make future queries more efficient)
INSERT INTO table_name( id, value )
SELECT 4, 100
FROM dual
WHERE NOT EXISTS(
SELECT 1
FROM table_name
WHERE id = 4 )
Or you could code a MERGE instead so that you update the VALUE column from 500 to 100 rather than inserting a new row.
Try MERGE statement:
MERGE INTO tbl USING
(SELECT 4 id, 100 value FROM dual) data
ON (data.id = tbl.id)
WHEN NOT MATCHED THEN
INSERT (id, value) VALUES (data.id, data.value)
INSERT INTO YOUR_TABLE (YOUR_FIELD)
SELECT '1' FROM YOUR_TABLE YT WHERE YT.YOUR_FIELD <> '1' LIMIT 1
Of course, that '1' will be your number or your variable.
You can use INSERT + SELECT to solve this problem.