I have a table called Student with these columns:
First name
Last name
Class ID
Class
I want to create an ID for student, I tried using Row_number Over Partiton but I seem to not be getting what looking for but more of it resetting itself, this is outcome I am trying to get.
First Last Class ClasID ID (outcome looking for)
------------------------------------
John Brown Math M21 ID01
John Brown English E31 ID01
Tom Bank Math M21 ID02
John Brown Gym G41 ID01
Tim Brown English E31 ID03
Use dense_rank():
select t.*, dense_rank() over(order by last, first) as id
from student
This assigns the same id to rows where the first and last names are the same.
If you really want a format like IDnn (which does not makes a lot of sense in my view), then you can format and concatenate:
select t.*,
'ID' || to_char(dense_rank() over(order by last, first), '09') as id
from student
This will fail to generate the proper string if there are more than 99 distinct students.
The dense_rank solution offered by #GMB produces an "id" as part of a given result set, not as an inherent identifier of a given student. The classic method of permanently assigning an ID to an entity (student, employee, class, etc) is to create a SEQUENCE, then an on_insert TRIGGER to populate the ID column with the next_value of the sequence.
SQL> CREATE TABLE STUDENTS
2 (
3 STUDENT_ID NUMBER NOT NULL
4 , FIRST_NAME VARCHAR2(20)
5 , LAST_NAME VARCHAR2(20)
6 , CONSTRAINT STUDENTS_PK PRIMARY KEY
7 (
8 STUDENT_ID
9 )
10 ENABLE
11 );
Table created.
Elapsed: 00:00:00.05
SQL> --
SQL> CREATE SEQUENCE id_seq
2 START WITH 1
3 INCREMENT BY 1
4 ;
Sequence created.
Elapsed: 00:00:00.01
SQL> CREATE OR REPLACE TRIGGER STUDENTS_TRIG
2 BEFORE INSERT ON students
3 FOR EACH ROW
4 BEGIN
5 :new.student_ID := ID_SEQ.NEXTVAL;
6 END;
7 /
Trigger created.
Elapsed: 00:00:00.02
SQL> show errors
No errors.
SQL> --
SQL> insert into students (first_name,
2 last_name)
3 values ('Ed',
4 'Stevens'
5 )
6 ;
1 row created.
Elapsed: 00:00:00.18
SQL> commit;
Commit complete.
Elapsed: 00:00:00.01
SQL> select * from students;
STUDENT_ID FIRST_NAME LAST_NAME
---------- -------------------- --------------------
1 Ed Stevens
1 row selected.
Elapsed: 00:00:00.07
SQL> --
SQL> drop trigger students_trig;
Trigger dropped.
Elapsed: 00:00:00.02
SQL> drop table students purge;
Table dropped.
Elapsed: 00:00:00.05
SQL> drop sequence id_seq;
Sequence dropped.
Elapsed: 00:00:00.01
Starting with version 12, you don't have to create the sequence or trigger, just declare the ID column to be an 'identity' column. I leave it as an exercise for the student to research that concept.
Related
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.
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.
How I do create column ID with value JASG1?
I am only find example like this :
select 'JASG'||to_char(mtj_id_seq.nextval) from talend_job
Although what you wrote probably works (if there's a sequence named MTJ_ID_SEQ, you have a privilege to select from it; the same goes for the TALEND_JOB table), I'd say that it isn't what you should use.
Here's why: I'll create a table and a sequence. Table will be pre-populated with some IDs (just to put something in there).
SQL> create sequence mtj_id_seq;
Sequence created.
SQL> create table talend_job as
2 select rownum id from dept;
Table created.
SQL> select * from talend_job;
ID
----------
1
2
3
4
OK; 4 rows so far. Now, run your SELECT:
SQL> select 'JASG'||to_char(mtj_id_seq.nextval) from talend_job;
'JASG'||TO_CHAR(MTJ_ID_SEQ.NEXTVAL)
--------------------------------------------
JASG1
JASG2
JASG3
JASG4
SQL> select 'JASG'||to_char(mtj_id_seq.nextval) from talend_job;
'JASG'||TO_CHAR(MTJ_ID_SEQ.NEXTVAL)
--------------------------------------------
JASG5
JASG6
JASG7
JASG8
SQL>
See? You didn't get only 1 JASGx value, but as many as number of rows in the TALEND_JOB table. If there was a million rows, you'd get a million JASGx rows as well.
Therefore, maybe you meant to use DUAL table instead? E.g.
SQL> select 'JASG'||to_char(mtj_id_seq.nextval) from dual;
'JASG'||TO_CHAR(MTJ_ID_SEQ.NEXTVAL)
--------------------------------------------
JASG9
SQL> select 'JASG'||to_char(mtj_id_seq.nextval) from dual;
'JASG'||TO_CHAR(MTJ_ID_SEQ.NEXTVAL)
--------------------------------------------
JASG10
SQL>
See? Only one value.
Also, notice that sequences will provide unique values, but you can't rely on them being gapless.
As you mentioned "how to create column ID" - one option is to use a trigger. Here's an example:
SQL> create table talend_job (id varchar2(20), name varchar2(20)
Table created.
SQL> create or replace trigger trg_bi_tj
2 before insert on talend_job
3 for each row
4 begin
5 :new.id := 'JASG' || mtj_id_seq.nextval;
6 end;
7 /
Trigger created.
Let's insert some names; IDs should be auto-populated by the trigger:
SQL> insert into talend_job (name) values ('littlefoot');
1 row created.
SQL> insert into talend_job (name) values ('Ishak');
1 row created.
SQL> select * From talend_job;
ID NAME
-------------------- --------------------
JASG11 littlefoot
JASG12 Ishak
SQL>
OK then; now you have some more info - read and think about it.
By the way, what is the "compiler-errors" tag used for? Did you write any code and it failed? Perhaps you'd want to share it with us.
I have a table:
id
name
amount
If the same name was inserted, then amount should be incremented.
Else, insert with 0 amount.
How to create a trigger with a given condition?
You shouldn't really be doing that. AMOUNT column effectively counts number of NAME appearances in that table. You can always count it, can't you? So, what's the purpose of doing that?
If you want to know which name was inserted prior (or after) some other name (equal to the previous one), sort them by ID (if it is incremental, such as an identity column or if it gets its value from a sequence).
If ID isn't incremental, add DATE_INSERTED column (and sort by it; or apply ROW_NUMBER analytic function which orders values by DATE_INSERTED).
Also, what happens if you delete one of those duplicate names? Will you retroactively decrement AMOUNT column for all previous instances of that NAME?
But, if you insist, here's one option. As you can't just select from a table you're inserting into (because of the mutating table error), I'm using a compound trigger. The part of "insert amount 0" is done by setting the column's default value (you don't need any code for that).
SQL> create table test
2 (id number primary key,
3 name varchar2(20),
4 amount number default 0);
Table created.
SQL> create or replace trigger trg_ai_test
2 for insert on test
3 compound trigger
4
5 type test_rt is record (id test.id%type,
6 name test.name%type);
7 type rli_t is table of test_rt index by pls_integer;
8 g_rli rli_t;
9
10 after each row is
11 begin
12 g_rli(g_rli.count + 1).id := :new.id;
13 g_rli(g_rli.count).name := :new.name;
14 end after each row;
15
16 after statement is
17 l_cnt number;
18 begin
19 for i in 1 .. g_rli.count loop
20 dbms_output.put_Line('x');
21 dbms_output.put_line(i ||' '|| g_rli(i).id ||' '||g_rli(i).name);
22 select count(*) into l_cnt
23 from test
24 where name = g_rli(i).name;
25
26 update test set
27 amount = l_cnt
28 where id = g_rli(i).id;
29 end loop;
30
31 end after statement;
32 end trg_ai_test;
33 /
Trigger created.
SQL>
Testing:
SQL> insert into test (id, name) values (1, 'little');
1 row created.
SQL> insert into test (id, name) values (2, 'foot');
1 row created.
SQL> insert into test (id, name) values (3, 'little');
1 row created.
SQL> insert into test (id, name) values (9, 'little');
1 row created.
SQL> select * from test;
ID NAME AMOUNT
---------- -------------------- ----------
1 little 1
2 foot 1
3 little 2
9 little 3
SQL>
Or, a simpler solution (described above), without using that much code:
SQL> select id, name,
2 row_number() over (partition by name order by id) as amount
3 from test;
ID NAME AMOUNT
---------- -------------------- ----------
2 foot 1
1 little 1
3 little 2
9 little 3
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)