I want to insert the records for a particular column that would be increment by 1 whenever the new row gets inserted into table based on the following condition For current year the first row with value :1 For current year the column value should be increment by 1 meaning 1 for 1st record of that current year and next available number for the matching year
Year Value
2016 1
2016 2
2016 3
2017 1
2017 2
...
My approach will be somewhat like this:
INSERT INTO ABC(ANALYSIS_YEAR,ANALYSIS_NUMBER)
values (EXTRACT(YEAR FROM sysdate),
case when ANALYSIS_YEAR=EXTRACT(YEAR FROM sysdate) then AutoIcreamt with starting value 1 else 1;
)
Any solution that looks at current table values will not work in a 'real' environment with multiple users and multiple sessions and parallel transactions.
I think you need to separate out the two requirements:
The first is handled using a sequence as these are designed exactly for this and handle concurrency (multiple users, multiple transactions, ...).
The second is a reporting requirement and has a number of options depending on performance requirements.
First of all create a sequence:
create sequence seq_analysis_id start with 1 increment by 1 nocache nocycle;
Not let's create a base table and a trigger to handle the auto-increment:
create table analysis_data (
analysis_id integer not null,
analysis_date date not null
);
alter table analysis_data add constraint pk_analysis_data primary key (analysis_id);
create or replace trigger trg_analysis_data
before insert on analysis_data
for each row
begin
:new.analysis_id := seq_analysis_id.nextval();
end trg_analysis_data;
/
insert into analysis_data (analysis_date) values (to_date('2015-12-28', 'YYYY-MM-DD'));
insert into analysis_data (analysis_date) values (to_date('2015-12-29', 'YYYY-MM-DD'));
insert into analysis_data (analysis_date) values (to_date('2015-12-30', 'YYYY-MM-DD'));
insert into analysis_data (analysis_date) values (to_date('2015-12-31', 'YYYY-MM-DD'));
insert into analysis_data (analysis_date) values (to_date('2016-01-01', 'YYYY-MM-DD'));
insert into analysis_data (analysis_date) values (to_date('2016-01-02', 'YYYY-MM-DD'));
insert into analysis_data (analysis_date) values (to_date('2016-01-03', 'YYYY-MM-DD'));
commit;
select * from analysis_data;
ANALYSIS_ID ANALYSIS_DATE
1 28/12/2015
2 29/12/2015
3 30/12/2015
4 31/12/2015
5 01/01/2016
6 02/01/2016
7 03/01/2016
Ok - so that all works fine but doesn't give you what you asked for :)
This is the second part - the reporting requirement:
The first option is just to get the numbers you need dynamically:
select
analysis_id,
analysis_date,
extract(year from analysis_date) analysis_year,
row_number()
over (partition by trunc(analysis_date, 'YYYY')
order by analysis_date, analysis_id) analysis_number
from
analysis_data;
Using Analytic Functions ( row_number
in this case) is a great way to handle this sort of thing.
ANALYSIS_ID ANALYSIS_DATE ANALYSIS_YEAR ANALYSIS_NUMBER
1 28/12/2015 2015 1
2 29/12/2015 2015 2
3 30/12/2015 2015 3
4 31/12/2015 2015 4
5 01/01/2016 2016 1
6 02/01/2016 2016 2
7 03/01/2016 2016 3
I've ordered by analysis_date, analysis_id
in the row_number
function. This probably isn't necessary but would be needed if you had to handle updates to the analysis_date
(in which case the sequence no longer works for in-year ordering on its own).
You could make this a little more straightforward for reporting by wrapping it in a view:
create or replace view analysis_data_v as
select
analysis_id,
analysis_date,
extract(year from analysis_date) analysis_year,
row_number()
over (partition by trunc(analysis_date, 'YYYY')
order by analysis_date, analysis_id) analysis_number
from
analysis_data;
This may be all you need, but if you have large data sets then you may need to pre-calculate some of these values. You have virtual columns in 11g, but these don't work for analytic functions. My option here would be to use a materialized view - lots of ways to handle materialized view refreshes and the simplest would be:
create materialized view analysis_data_mv
build immediate
refresh complete on demand
as
select
analysis_id,
analysis_date,
analysis_year,
analysis_number
from
analysis_data_v;
select * from analysis_data_mv order by analysis_year, analysis_number;
ANALYSIS_ID ANALYSIS_DATE ANALYSIS_YEAR ANALYSIS_NUMBER
1 28/12/2015 2015 1
2 29/12/2015 2015 2
3 30/12/2015 2015 3
4 31/12/2015 2015 4
5 01/01/2016 2016 1
6 02/01/2016 2016 2
7 03/01/2016 2016 3
In this case the materialized view would be manually refreshed:
exec dbms_mview.refresh('analysis_data_mv');
Hope this helps.
You will probably need a trigger on insert that looks like this (I guess you can't change the value of Year, in that case it will get more complicated). Like @ibre5041 This won't work on a multi-user enviroment since you could have duplicates if a couple of transactions are executed at the same time on the same year:
CREATE OR REPLACE TRIGGER trg_bi_table1
before insert
ON table_1
Begin
select nvl(max(value),0)+1 as value
into :new.value
from table_1
where Year = :new.Year;
end;
/
In a multi-user enviroment you should use select for update
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.