Auto incrementing column including year - sql

I need to create a table with an ID column that increments automatically and concatenates the current year, for example:
year = 2018 ==> 20181 , 20182 , 20183 , etc
year = 2020 ==> 20201 , 20202 , 20203 , etc
How can I do that?

Firstly create a sequence :
CREATE SEQUENCE seq_myTable START WITH 1;
and then use it in a trigger :
CREATE OR REPLACE TRIGGER Trg_myTable
BEFORE INSERT ON myTable
FOR EACH ROW
DECLARE
v_year varchar2(4):=to_char(sysdate,'yyyy');
BEGIN
:new.id := v_year||seq_myTable.nextval;
END;
/

The following requires Oracle 12.1 or later:
create sequence demo_seq;
create table demo
( genid varchar2(10) default on null to_char(sysdate,'YYYY')||demo_seq.nextval
constraint demo_pk primary key
, somecol varchar2(10) );
insert into demo (somecol) values ('Kittens');
insert into demo (somecol) values ('Puppies');
select * from demo;
GENID SOMECOL
---------- ----------
20181 Kittens
20182 Puppies
The only limitation here is that the sequence doesn't restart for each year. To automate this, I think you would have to abandon the sequence and use a select max +1 approach with some explicit serialisation (dbms_lock or similar). Alternatively, schedule a job to restart the sequence each year.

I guess there is no such way to do this with automatic mechanisms as a generated Id.
I see a few options here:
use 2 columns: Id and Year
use a virtual column
use a function in a on insert trigger to build your custom id
I'd recommend using 2 columns and a primary key over the id column and a index over the year column as it's the most clean implementation i can think of (and perhaps the fastest as well).
+ you should use your id as id and another column to see which year it's been added, or even a date column if you need the information the record has been inserted

Related

Postgresql how to create a custom id column formatted with character, date and number

CREATE SEQUENCE customers_seq;
CREATE TABLE customers (
id INT NOT NULL PRIMARY KEY DEFAULT NEXTVAL ('customers_seq'),custom_id VARCHAR(100),test varchar(10)
)
I need the sequence to go like so:
CU22_001
CU22_002
CU22_003
Explanation
CU - Is Stable Character
22 - Todays year date format (yy)
001 - Increments by +1 when a new value is added.
Next year (2023)..
the sequence needs to be reset back to 001 but the 20 changes to 21 because of the year being 2022 :
CU23_001
CU23_002
CU23_003
Question
How can I create this custom ID column BEFORE INSERT the new row?
After Postgres version 12, we can try to use Generated Columns
The generation expression can only use immutable functions.
So that we need to create generate_custom_id immutable function which generate your expect custom_id format.
CREATE FUNCTION generate_custom_id(id INT)
returns text
as
$$
SELECT 'CU' || TO_CHAR(now(), 'YY')|| '_' || TO_CHAR(id,'FM000');
$$
language sql
immutable;
then use that in your custom_id column
CREATE SEQUENCE customers_seq;
CREATE TABLE customers (
id INT NOT NULL PRIMARY KEY DEFAULT NEXTVAL ('customers_seq'),
custom_id VARCHAR(100) GENERATED ALWAYS AS ( generate_custom_id(id)) STORED,
test varchar(10)
);
sqlfiddle

How to create something to update end date by starting day and duration column automaticly SQL

I want to create something that will update my data in table automaticly but i don't know what to use.
i have nr1 table
create table NR1
(
id INTEGER not null,
price INTEGER,
price2 INTEGER,
start_date DATE,
end_date DATE,
duration NUMBER
)
and i tried to create trigger which will update always after inserting or updateing my table, end_date, so i tried something like this:
create or replace trigger update_nr1_date
after update or insert on nr1
for each row
when (new.id>0)
declare
begin
UPDATE nr1
set nr1.end_date =add_months(nr1.start_date, nr1.duration);
end;
but it have mutation problems, and i read something about that, and i udersatnd the concept of this. I want to make trigger like this (not sheduler, because i want to get it automaticly after inserting or updating some rows). Is it possible while inserting data to table it itself refill missing columns?
Your trigger needs to before update in order to change the value in the row the trigger is fired again; and it needs to modify the current row's (new) pseudorecord via an assignment statement - you should not be issuing a separate update statement inside the trigger. It will cascade, for a start, and in your example woudl try to update every row in the table. But that is also causing the mutating table error - you're trying to update rows in the table the trigger is against. There are workarounds when that is actually necessary, but it isn't here, that update should just not be there.
So you would do:
create or replace trigger update_nr1_date
before update or insert on nr1
for each row
when (new.id>0)
begin
:new.end_date := add_months(:new.start_date, :new.duration);
end;
/
But if you're on 11g or higher you can use a virtual column instead, without needing a trigger at all:
create table NR1
(
id INTEGER not null,
price INTEGER,
price2 INTEGER,
start_date DATE,
end_date DATE generated always as (add_months(start_date, duration)) virtual,
duration NUMBER
)
Then when you insert, skip that column in the statement:
insert into nr1 (id, price, price2, start_date, duration)
values (1, 2, 3, date '2018-06-01', 3);
1 row inserted.
select * from nr1 where id = 1;
ID PRICE PRICE2 START_DATE END_DATE DURATION
---------- ---------- ---------- ---------- ---------- ----------
1 2 3 2018-06-01 2018-09-01 3
The end date always reflects the values of the other two columns.

SQL CREATE SEQUENCE based on a combination of date and counter

I am using Oracle SQL:
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production 0
PL/SQL Release 12.1.0.2.0 - Production 0
CORE 12.1.0.2.0 Production 0
TNS for Linux: Version 12.1.0.2.0 - Production 0
NLSRTL Version 12.1.0.2.0 - Production
I need help figuring out how to create a sequence for a key field below:
I have a table with a field named MY_ID.
MY_ID needs to be automatically generated using sequence when a record is inserted.
The rule for the sequence is a combination of a string prefix, an counter increment by 1 per day and reset at midnight, and the date.
for example:
on Sept 10, we inserted 2 records, then the MY_ID should be:
PREFIX_01_20170910
PREFIX_02_20170910
on Sept 11 :
PREFIX_01_20170911
PREFIX_02_20170911
PREFIX_03_20170911
on Sept 12, the whole table might look like this
PREFIX_01_20170910
PREFIX_02_20170910
PREFIX_01_20170911
PREFIX_02_20170911
PREFIX_03_20170911
PREFIX_01_20170912
so far, all i can do with the sequence is increment by 1 regardless of the date:
CREATE SEQUENCE SEQ_MY_TABLE INCREMENT BY 1 MAXVALUE 9999999999999999999999999999 MINVALUE 1 NOCACHE;
and the trigger:
create or replace TRIGGER MY_TABLE_TRG
BEFORE INSERT ON MY_TABLE
FOR EACH ROW
BEGIN
<<COLUMN_SEQUENCES>>
BEGIN
IF INSERTING AND :NEW.MY_ID IS NULL THEN
SELECT SEQ_MY_TABLE .NEXTVAL INTO :NEW.MY_ID FROM SYS.DUAL;
END IF;
END COLUMN_SEQUENCES;
END;
Thank you!
You can probably dream up a scheme to generate this PREFIX_nn_date key that will appear to work in your development environment, but I'm going to give you a bit of unwanted advice: don't waste your time on it. Make your primary key a simple NUMBER column, populate it from a sequence, and move on. Once your code hits production, where many more users will be banging on your table simultaneously, your carefully crafted scheme to generate that PREFIX_nn_date key is likely to fail - and the more band-aids you throw at it to fix the problems the worse it's going to become.
Best of luck.
You can modify the TRIGGER as follows. Since you need each digit in the sequence as 01,02,03 attached to your prefix, I have used fm specifier in TO_CHAR with '00'. If the total inserts per day exceeds 99 , you need to use fm000
create or replace TRIGGER MY_TABLE_TRG
BEFORE INSERT ON MY_TABLE
FOR EACH ROW
DECLARE
BEGIN
<<COLUMN_SEQUENCES>>
BEGIN
IF INSERTING AND :NEW.MY_ID IS NULL THEN
SELECT 'PREFIX_'||TO_CHAR(SEQ_MY_TABLE.NEXTVAL,'fm00')||'_'||TO_CHAR(SYSDATE,'YYYYMMDD') INTO :NEW.MY_ID FROM SYS.DUAL;
END IF;
END COLUMN_SEQUENCES;
END;
Note: To suggest a good practice, I do not recommend this to be used as your PRIMARY KEY. It would be better to simply make the sequence PRIMARY KEY in all your application code while populating the records.
Following up on Bob Jarvis's answer, you can always create your string when you need it. Here is a simple example.
with records as (
select 1 id, to_date('20170901', 'yyyymmdd') theDate
from dual
union
select 2 id, to_date('20170901', 'yyyymmdd') theDate
from dual
union
select 3 id, to_date('20170902', 'yyyymmdd') theDate
from dual
)
select 'prefix_' ||
to_char(theDate, 'yyyymmdd') || '_' ||
to_char( rank() over (partition by theDate order by id)) prefix
from records
returns:
prefix_20170901_1
prefix_20170901_2
prefix_20170902_1
I don't do that much oracle work, but if you are going to do this repeatedly, you might want to incorporate this logic into a function or view.
The simpliest way is to recreate sequence every midnight via job. But using sequences is not a good idea. I think this ID is important for you, but sequence can cache some values, some values can be missing. So you'll get:
PREFIX_01_20170910
PREFIX_02_20170910
PREFIX_04_20170910
PREFIX_07_20170910
...
and so on. For example you had "cache 10", you inserted 2 records and quited, or did rollback, or something else.
Use just number for increment field and calc this fake ID.
you can create function like below to get new id and use it in the insert query.
CREATE OR REPLACE FUNCTION F_GETID (P_DT IN VARCHAR2) RETURN VARCHAR2
IS
V_NEW_ID VARCHAR2(50);
BEGIN
SELECT 'PREFIX_' || COUNT(*)+1 ||'_' || P_DT INTO V_NEW_ID FROM MY_TABLE
WHERE MY_ID LIKE 'PREFIX%'||P_DT;
RETURN V_NEW_ID;
END;
then
insert into my_table(my_id , ...) values(F_GETID('20170927'),...);

Orace: Default column value based on a filter

Hi a developer asked to add a column on a table which will have a default value of 'N', however if the entry has an id = 3 then the default value of this column should be 'Y', is there anyway I can achieve this in oracle?
I agree with the commenters who have mentioned that this is not a good database design. That said, making compromises with database design is not unusual in real-life situations.
I am not sure that a virtual column is what is wanted. The OP asked for a way to have a default; a virtual column works differently than a default constraint (e.g., with a default constraint we can insert a value other than the default into the column. The best route to take might be to use a trigger to set the "default" value:
CREATE OR REPLACE TRIGGER mytrigger
BEFORE INSERT ON mytable FOR EACH ROW
WHEN (new.mycolumn IS NULL)
BEGIN
SELECT DECODE(id, 3, 'Y', 'N') INTO :new.mycolumn FROM dual;
END;
/
A trigger will also work whether you're using Oracle 10g or 11g (both of which you've tagged).
Hope this helps.
11g approach
From Oracle 11g and up, you could do this in one step using VIRTUAL columns.
Test case
SQL> CREATE TABLE tab_default (
2 ID NUMBER,
3 flag varchar2(1) GENERATED ALWAYS AS (decode(id, 3, 'Y', 'N')) VIRTUAL
4 );
Table created.
SQL>
SQL> INSERT INTO tab_default (ID) VALUES (1);
1 row created.
SQL> INSERT INTO tab_default (ID) VALUES (3);
1 row created.
SQL> INSERT INTO tab_default (ID) VALUES (10);
1 row created.
SQL> SELECT * FROM tab_default;
ID F
---------- -
1 N
3 Y
10 N
SQL>
So, the DECODE function in the VIRTUAL column declaration handles the requirement for you.
10g approach
You could fulfill the requirement using -
DEFAULT value
AFTER INSERT TRIGGER whenever id = 3
Create the table to have DEFAULT value as 'N'. Let the trigger fire only when a new row is inserted with value in id column = 3, such that the trigger updates the value to 'Y'. Else for all other cases the default value would be 'N'.
After adding new column to your table you can insert value in column using below query :
update table_name set column_name = ( case when id = 3 then 'Y' else 'N' end );
At the of inserting new records you may use below approach :
1) Decide column at the time of creating insert query, you can add logic for that when you create query.
2) Create a trigger in database that should update you column value after inserting any new row to table.
This is a very poor database design. It doesn't respect relational database normal form.
I suggest keeping that table as it is and create a new view on the table with an extra column which is calculated using DECODE or CASE WHEN ...
Create a new table with the additional value column:
create table table1 as
select u.*,
case when id=3 then 'Y' ELSE 'N'
END value
from table2 u

Oracle - Insert New Row with Auto Incremental ID

I have a workqueue table that has a workid column. The workID column has values that increment automatically. Is there a way I can run a query in the backend to insert a new row and have the workID column increment automatically?
When I try to insert a null, it throws error ORA01400 - Cannot insert null into workid.
insert into WORKQUEUE (facilitycode,workaction,description) values ('J', 'II', 'TESTVALUES')
What I have tried so far - I tried to look at the table details and didn't see any auto-increment. The table script is as follow
"WORKID" NUMBER NOT NULL ENABLE,
Database: Oracle 10g
Screenshot of some existing data.
ANSWER:
I have to thank each and everyone for the help. Today was a great learning experience and without your support, I couldn't have done. Bottom line is, I was trying to insert a row into a table that already has sequences and triggers. All I had to do was find the right sequence, for my question, and call that sequence into my query.
The links you all provided me helped me look these sequences up and find the one that is for this workid column. Thanks to you all, I gave everyone a thumbs up, I am able to tackle another dragon today and help patient care take a step forward!"
This is a simple way to do it without any triggers or sequences:
insert into WORKQUEUE (ID, facilitycode, workaction, description)
values ((select max(ID)+1 from WORKQUEUE), 'J', 'II', 'TESTVALUES')
It worked for me but would not work with an empty table, I guess.
To get an auto increment number you need to use a sequence in Oracle.
(See here and here).
CREATE SEQUENCE my_seq;
SELECT my_seq.NEXTVAL FROM DUAL; -- to get the next value
-- use in a trigger for your table demo
CREATE OR REPLACE TRIGGER demo_increment
BEFORE INSERT ON demo
FOR EACH ROW
BEGIN
SELECT my_seq.NEXTVAL
INTO :new.id
FROM dual;
END;
/
There is no built-in auto_increment in Oracle.
You need to use sequences and triggers.
Read here how to do it right. (Step-by-step how-to for "Creating auto-increment columns in Oracle")
ELXAN#DB1> create table cedvel(id integer,ad varchar2(15));
Table created.
ELXAN#DB1> alter table cedvel add constraint pk_ad primary key(id);
Table altered.
ELXAN#DB1> create sequence test_seq start with 1 increment by 1;
Sequence created.
ELXAN#DB1> create or replace trigger ad_insert
before insert on cedvel
REFERENCING NEW AS NEW OLD AS OLD
for each row
begin
select test_seq.nextval into :new.id from dual;
end;
/ 2 3 4 5 6 7 8
Trigger created.
ELXAN#DB1> insert into cedvel (ad) values ('nese');
1 row created.
You can use either SEQUENCE or TRIGGER to increment automatically the value of a given column in your database table however the use of TRIGGERS would be more appropriate. See the following documentation of Oracle that contains major clauses used with triggers with suitable examples.
Use the CREATE TRIGGER statement to create and enable a database trigger, which is:
A stored PL/SQL block associated with a table, a schema, or the
database or
An anonymous PL/SQL block or a call to a procedure implemented in
PL/SQL or Java
Oracle Database automatically executes a trigger when specified conditions occur. See.
Following is a simple TRIGGER just as an example for you that inserts the primary key value in a specified table based on the maximum value of that column. You can modify the schema name, table name etc and use it. Just give it a try.
/*Create a database trigger that generates automatically primary key values on the CITY table using the max function.*/
CREATE OR REPLACE TRIGGER PROJECT.PK_MAX_TRIGGER_CITY
BEFORE INSERT ON PROJECT.CITY
FOR EACH ROW
DECLARE
CNT NUMBER;
PKV CITY.CITY_ID%TYPE;
NO NUMBER;
BEGIN
SELECT COUNT(*)INTO CNT FROM CITY;
IF CNT=0 THEN
PKV:='CT0001';
ELSE
SELECT 'CT'||LPAD(MAX(TO_NUMBER(SUBSTR(CITY_ID,3,LENGTH(CITY_ID)))+1),4,'0') INTO PKV
FROM CITY;
END IF;
:NEW.CITY_ID:=PKV;
END;
Would automatically generates values such as CT0001, CT0002, CT0002 and so on and inserts into the given column of the specified table.
SQL trigger for automatic date generation in oracle table:
CREATE OR REPLACE TRIGGER name_of_trigger
BEFORE INSERT
ON table_name
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
SELECT sysdate INTO :NEW.column_name FROM dual;
END;
/
the complete know how, i have included a example of the triggers and sequence
create table temasforo(
idtemasforo NUMBER(5) PRIMARY KEY,
autor VARCHAR2(50) NOT NULL,
fecha DATE DEFAULT (sysdate),
asunto LONG );
create sequence temasforo_seq
start with 1
increment by 1
nomaxvalue;
create or replace
trigger temasforo_trigger
before insert on temasforo
referencing OLD as old NEW as new
for each row
begin
:new.idtemasforo:=temasforo_seq.nextval;
end;
reference:
http://thenullpointerexceptionx.blogspot.mx/2013/06/llaves-primarias-auto-incrementales-en.html
For completeness, I'll mention that Oracle 12c does support this feature. Also it's supposedly faster than the triggers approach. For example:
CREATE TABLE foo
(
id NUMBER GENERATED BY DEFAULT AS IDENTITY (
START WITH 1 NOCACHE ORDER ) NOT NULL ,
name VARCHAR2 (50)
)
LOGGING ;
ALTER TABLE foo ADD CONSTRAINT foo_PK PRIMARY KEY ( id ) ;
Best approach: Get the next value from sequence
The nicest approach is getting the NEXTVAL from the SEQUENCE "associated" with the table. Since the sequence is not directly associated to any specific table,
we will need to manually refer the corresponding table from the sequence name convention.
The sequence name used on a table, if follow the sequence naming convention, will mention the table name inside its name. Something likes <table_name>_SEQ. You will immediately recognize it the moment you see it.
First, check within Oracle system if there is any sequence "associated" to the table
SELECT * FROM all_sequences
WHERE SEQUENCE_OWNER = '<schema_name>';
will present something like this
Grab that SEQUENCE_NAME and evaluate the NEXTVAL of it in your INSERT query
INSERT INTO workqueue(id, value) VALUES (workqueue_seq.NEXTVAL, 'A new value...')
Additional tip
In case you're unsure if this sequence is actually associated with the table, just quickly compare the LAST_NUMBER of the sequence (meaning the current value) with the maximum id of
that table. It's expected that the LAST_NUMBER is greater than or equals to the current maximum id value in the table, as long as the gap is not too suspiciously large.
SELECT LAST_NUMBER
FROM all_sequences
WHERE SEQUENCE_OWNER = '<schema_name>' AND SEQUENCE_NAME = 'workqueue_seq';
SELECT MAX(ID)
FROM workqueue;
Reference: Oracle CURRVAL and NEXTVAL
Alternative approach: Get the current max id from the table
The alternative approach is getting the max value from the table, please refer to Zsolt Sky answer in this same question
This is a simple way to do it without any triggers or sequences:
insert into WORKQUEUE (ID, facilitycode, workaction, description)
values ((select count(1)+1 from WORKQUEUE), 'J', 'II', 'TESTVALUES');
Note : here need to use count(1) in place of max(id) column
It perfectly works for an empty table also.