SQL Insert syntax with max(value)+1 - oracle

Using Oracle DB
Trying to create logic where when inserting a new row the logic checks if there is an existing numerical value. If there is a value then the logic would perform a max(value)+1. If there is no value then INSERT '1'.

I would suggest that you use a sequence instead of looking for the max value + 1.
A sequence would take care of the incrementing for you. http://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_6015.htm
Example:
CREATE SEQUENCE MY_SEQ START WITH 1 INCREMENT BY 1;
Insert like
INSERT INTO MY_TABLE (ID, WIDGET) VALUES (NEXTVAL FOR MY_SEQ, 'asdf');

Related

adding data from two different tables using a trigger

I created three tables A (id, name, date, realnumber, integer), B (id, name, date, realnumber, integer), and C which is identical to table A. It only has two more columns called integerB and sequence s. I want to create a trigger which would fire after insert on table B for each row input so that it saves the referenced row of Table A and adds integer from input row of table B in column integerB of table C. If the row already exists in Table C only integerB should be added. When it comes to sequence s, next value is added with first insert of row of table A.
Simple explanation: Table C is a copy of table A with two additional columns: integerB and sequence. The point of the trigger is to add new rows from table A without repetition, integerB from table B(integer in table B) and sequence should start with 1 and increment by 1. If the row in table A is repeated then only integerB should be updated.
I did not work with triggers that much, so I am not sure how to solve the problem when I have to insert data from multiple tables. Here is my trigger.
CREATE OR REPLACE TRIGGER trig1
AFTER INSERT ON B
FOR EACH ROW
INSERT INTO C (integerB) VALUES (NEW.integer);
INSERT INTO C (id, name, date, realnumber)
SELECT a.id, a.name, a.date, a.realnumber FROM A a;
END;
/
First off you really need to use better column and table names as a lot of these are reserved words... This makes everything far more complicated than it needs to be.
It isn't entirely clear what you want to do but it seems that if someone was to insert a record with ID = 1 into B then you want to get the values from A for ID = 1 and store them in C along with the integer value inserted into B
In which case you want to use an MERGE statement (UPSERT) in your trigger and something like
CREATE OR REPLACE TRIGGER T1
AFTER INSERT ON B
FOR EACH ROW
BEGIN
MERGE INTO C C
USING (SELECT * FROM A WHERE ID = :NEW.ID) A
ON (C.ID = :NEW.ID)
WHEN MATCHED THEN UPDATE
SET C.INTEGERB = :NEW.INTEGER,
C.SEQUENCE = C.SEQUENCE + 1
WHEN NOT MATCHED THEN
INSERT (ID, NAME, DATE, REALNUMBER, INTEGER, INTEGERB, SEQUENCE)
VALUES (A.ID, A.NAME. A.DATE, A.REALNUMBER, A.INTEGER, :NEW.INTEGER, 0);
END;
/
For sequence this has been set to 0 when a new record is inserted into C and then incremented each time integerB is updated. I am not sure if this is waht you want or not.
You should be able to tweak this to match the exact joins and logic you need.
Tip
Get your SQL statement working with literal values first and then translate it into your trigger. It will be much easier if you can get something working manually first before you attempt to make things more complicated

Inserting Row Number based on existing value in the table

I have a requirement that I need to insert row number in a table based on value already present in the table. For example, the max row_nbr record in the current table is something like this:
+----------+----------+------------+---------+
| FST_NAME | LST_NAME | STATE_CODE | ROW_NBR |
+----------+----------+------------+---------+
| John | Doe | 13 | 123 |
+----------+----------+------------+---------+
Now, I need to insert more records, with given FST_NAME and LST_NAME values. ROW_NBR needs to be generated while inserting the data into table with values auto-incrementing from 123.
I can't use a sequence, as my loading process is not the only process that inserts data into this table. And I can't use a cursor as well, as due to high volume of data the TEMP space gets filled up quickly. And I'm inserting data as given below:
insert into final_table
( fst_name,lst_name,state_code)
(select * from staging_table
where state_code=13);
Any ideas how to implement this?
It sounds like other processes are finding the current maximum row_nbr value and incrementing it as they do single-row inserts in a cursor loop.
You could do something functionally similar, either finding the maximum in advance and incrementing it (if you're already running this in a PL/SQL block):
insert into final_table (fst_name, lst_name, state_code, row_nbr)
select st.*, variable_holding_maximum + rownum
from staging_table st
where st.state_code=13;
or by querying the table as part of the query, which doesn't need PL/SQL:
insert into final_table (fst_name, lst_name, state_code, row_nbr)
select st.*, (select max(row_nbr) from final_table) + rownum
from staging_table st
where st.state_code=13;
db<>fiddle
But this isn't a good solution because it doesn't prevent clashes from different processes and sessions trying to insert at the same time; but neither would the cursor loop approach, unless it is catching unique constraint errors and re-attempting with a new value, perhaps.
It would be better to use a sequence, which would be an auto-increment column but you said you can't change the table structure; and you need to let the other processes continue to work without modification. You can still do that with a sequence and trigger approach, having the trigger always set the row_nbr value form the sequence, regardless of whether the insert statement supplied a value.
If you create a sequence that starts from the current maximum, with something like:
create sequence final_seq start with <current max + 1>
or without manually finding it:
declare
start_with pls_integer;
begin
select nvl(max(row_nbr), 0) + 1 into start_with from final_table;
execute immediate 'create sequence final_seq start with ' || start_with;
end;
/
then your trigger could just be:
create trigger final_trig
before insert on final_table
for each row
begin
:new.row_nbr := final_seq.nextval;
end;
/
Then your insert ... select statement doesn't need to supply or even think about the row_nbr value, so you can leave it as you have it now (except I'd avoid select * even in that construct, and list the staging table columns explicitly); and any existing inserts that do supply the row_nbr don't need to be modified and the value they supply will just be overwritten from the sequence.
db<>fiddle showing inserts with and withouth row_nbr specified.

Increment new column after insert

I have a question for all of you. I'm quite new in SQL and searched for more than 2 hours and didn't find exactly what I need.
I'm having a table in SQL named Courses. Here is the constructor:
CREATE TABLE Courses
(sign VARCHAR2(6) NOT NULL,
title VARCHAR(50) NOT NULL,
credits INTEGER NOT NULL,
CONSTRAINT PrimaryKeyCourses PRIMARY KEY (sign)
);
I have to add a new column, which I did with :
ALTER TABLE Courses ADD frequency INTEGER;
I want to create a trigger which will increment every time a new courses is added.
I tried to do this :
CREATE TRIGGER fq
AFTER INSERT ON Courses
FOR EACH ROW
UPDATE frequency SET frequency = frequency + 1;
But it doesn't seems to work properly :( I don't know what to do.
No need to use an UPDATE statement, use a SELECT statement with max(value)+1. And to be able to change a :new. value, need to convert trigger to BEFORE type.
So, you can use the one as below
CREATE OR REPLACE TRIGGER fq
BEFORE INSERT ON Courses
FOR EACH ROW
DECLARE
BEGIN
select nvl(max(frequency),0)+1
into :new.frequency
from Courses;
END;
Of course you need a commit after a DML statement, I think it's better to include only one commit outside of this trigger after INSERT statement applied on Courses table, because of providing transaction integrity rule.
P.S. I know you're restricted to use a trigger, but Using a sequence for the value of column frequency is a better, practical alternative as #nikhil sugandh suggested. In this case a trigger is not needed. If you're using DB version 12c, you can add that sequence as default for the column frequency as frequency INTEGER GENERATED ALWAYS AS IDENTITY during the table creation.
use sequence :
CREATE SEQUENCE Courses_frequency
MINVALUE 1
MAXVALUE 999999999999999999999999999
START WITH 1
INCREMENT BY 1
CACHE 20;
and do insert like:
INSERT INTO Courses
(sign,title,credits,frequency)
VALUES
(value1,value2,value3,Courses_frequency.NEXTVAL);

Insert in Merge not working in Oracle

I am new to Oracle. I have a table in Oracle which has 4 columns Period, Open_Flag,Creation_Dt,Updated_By.
The Period column is the Primary key of the table. I have created a proc which will check the value of period from input parameter in the table, if its existing, the value of Open_flag has to be updated else a new record shall be inserted.
create or replace
PROCEDURE PROC_REF_SAP_PERIOD(
V_PERIOD IN NUMBER,V_OPEN_FLAG IN VARCHAR2,V_CREATION_DT IN DATE,V_UPDATED_BY IN VARCHAR2)
AS
BEGIN
MERGE INTO REF_SAP_PERIOD T
USING (SELECT * FROM REF_SAP_PERIOD WHERE PERIOD=V_PERIOD )S
ON (T.PERIOD=S.PERIOD )
WHEN MATCHED THEN UPDATE SET OPEN_FLAG = V_OPEN_FLAG --WHERE PERIOD=V_PERIOD AND CREATION_DT=V_CREATION_DT AND UPDATED_BY=V_UPDATED_BY
WHEN NOT MATCHED THEN INSERT (PERIOD,OPEN_FLAG,CREATION_DT,UPDATED_BY) VALUES (V_PERIOD,V_OPEN_FLAG,V_CREATION_DT,V_UPDATED_BY);
END;
The issue is that the Update is working well in this case, however, the insert is not working. Please help.
You are merging table with itself, filtered by period. Obviously, it will never see your non-existent values in itself.
Try this line instead of your USING line:
using (select V_PERIOD "period" from dual)S

Using sequential values for the primary key in an INSERT query

How can I write an insert query for an Oracle database which has a sequential primary key so that the insert statement automatically takes the next number in the sequence?
INSERT INTO LD_USER_ROLE(USER_ROLE_ID,INS_USER,INS_DATE, USERNAME)
VALUES (100, 'sp22',to_date('2003/05/03 21:02:44','yyyy/mm/dd hh24:mi:ss'),'JOHN BARRY', )
In the above statement I have hardcoded the value of 100 for the key 'USER_ROLE_ID' but I'd like to alter this as explained in the first paragraph.
Why don't you just create a trigger for your sequence like this:
Sequence:
CREATE SEQUENCE LD_USER_ROLE_SEQ
INCREMENT BY 1 START WITH 1 NOMAXVALUE NOMINVALUE NOCYCLE NOCACHE NOORDER
Trigger:
CREATE TRIGGER LD_USER_ROLE_INSERT BEFORE INSERT ON LD_USER_ROLE
REFERENCING NEW AS NEW OLD AS OLD FOR EACH ROW
BEGIN
SELECT LD_USER_ROLE_SEQ.NEXTVAL INTO :NEW.USER_ROLE_ID FROM DUAL;
END;
The trigger will automatically get the next value/id on every insert (like auto_increment in mysql).
Apart from using a trigger, you can use a sequence directly in the insert statement:
CREATE SEQUENCE LD_USER_ROLE_SEQ;
INSERT INTO LD_USER_ROLE
(USER_ROLE_ID,INS_USER,INS_DATE, USERNAME)
VALUES
(ld_user_role_seq.nextval, 'sp22',to_date('2003/05/03 21:02:44','yyyy/mm/dd hh24:mi:ss'),'JOHN BARRY', )

Resources