Auto-increment in Oracle without using a trigger - oracle

What are the other ways of achieving auto-increment in oracle other than use of triggers?

You can create and use oracle sequences. The syntax and details are at
http://www.techonthenet.com/oracle/sequences.php
Also read the article
http://rnyb2.blogspot.com/2006/02/potential-pitfall-with-oracle-sequence.html
to understand the limitations with respect to AUTONUMBER in other RDBMS

If you don't need sequential numbers but only a unique ID, you can use a DEFAULT of SYS_GUID(). Ie:
CREATE TABLE xxx ( ID RAW(16) DEFAULT SYS_GUID() )

A trigger to obtain the next value from a sequence is the most common way to achieve an equivalent to AUTOINCREMENT:
create trigger mytable_trg
before insert on mytable
for each row
when (new.id is null)
begin
select myseq.nextval into :new.id from dual;
end;
You don't need the trigger if you control the inserts - just use the sequence in the insert statement:
insert into mytable (id, data) values (myseq.nextval, 'x');
This could be hidden inside an API package, so that the caller doesn't need to reference the sequence:
mytable_pkg.insert_row (p_data => 'x');
But using the trigger is more "transparent".

As far as I can recall from my Oracle days, you can't achieve Auto Increment columns without using TRIGGER. Any solutions out there to make auto increment column involves TRIGGER and SEQUENCE (I'm assuming you already know this, hence the no trigger remarks).

Create a sequence:
create sequence seq;
Then to add a value
insert into table (id, other1, other2)
values (seq.nextval, 'hello', 'world');
Note: Look for oracle docs for more options about sequences (start value, increment, ...)

From 12c you can use an identity column, which makes explicit the link between table and auto-increment; there's no need for a trigger or a sequence. The syntax would be:
create table <table_name> ( <column_name> generated as identity );

In addition to e.g. FerranB's answer:
It is probably worth to mention that, as opposed to how auto_incement works in MySQL:
sequences work database wide, so they can be used for multiple tables and the values are unique for the whole database
therefore: truncating a table does not reset the 'autoincrement' functionaltiy

If you don't really want to use a "trigger-based" solution, you can achieve the auto-increment functionality with a programmatical approach, obtaining the value of the auto increment key with the getGeneratedKeys() method.
Here is a code snippet for your consideration:
Statement stmt = null;
ResultSet rs = null;
stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
java.sql.ResultSet.CONCUR_UPDATABLE);
stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTable");
stmt.executeUpdate("CREATE TABLE autoIncTable ("
+ "priKey INT NOT NULL AUTO_INCREMENT, "
+ "dataField VARCHAR(64), PRIMARY KEY (priKey))");
stmt.executeUpdate("INSERT INTO autoIncTable (dataField) "
+ "values ('data field value')",
Statement.RETURN_GENERATED_KEYS);
int autoIncKeyFromApi = -1;
rs = stmt.getGeneratedKeys();
if (rs.next()) {
autoIncKeyFromApi = rs.getInt(1);
}
else {
// do stuff here
}
rs.close();
source: http://forums.oracle.com/forums/thread.jspa?messageID=3368856

SELECT max (id) + 1
FROM table

Related

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);

Oracle constraint within a sequence

I'm trying to create a constraint on a column that it must be within a sequence (i.e. col_name < seq.maxval)
I tried to use a regular constraint, but the column doesn't have anything to tie to - it is just a sequence, not a column in a table.
Checks can't reference any kind of query, so I don't think that would work either.
ALTER TABLE STE_FILECOLL ADD (
CONSTRAINT STE_FC_CLFC_REF_STEF_IDFILE
FOREIGN KEY (CLFILECOLL)
REFERENCES ????
ENABLE VALIDATE
);
I expect there is a way to make sure that the values of a column are within a sequence, but an hour of reading documentation and duckduckgoing have been fruitless, so I turn to here.
In Oracle DB version 12c, a sequence's next value might be set as default for a column :
create table STE_FILECOLL
(
col0 int default seq1.nextval not null,
col_name int
);
and then check constraints might be added to provide the desired condition as :
alter table STE_FILECOLL
add constraint STE_CC_CLFC_REF_STEF_IDFILE
check (col_name < col0);
The only sequence pseudocolumns are nextval and currval, so you can't use that kind of syntax. Inline constraints are pretty limited. I think your best bet is to use a trigger.
[...]
select as.maxval into l_maxval from all_sequences where sequence_name = 'my_sequence';
if :new.col_name > l_maxval then
raise_application_error( -20001, 'too big' );
end if;

How to update string value by string comparison condition PL/SQL

I want update all values in my tables, but this can kill my database
UPDATE Table_1
SET Value = 'Some string with but changed'
where value = 'Some string without changes';
Can I do this by procedures, and it guarantee that it will not perform in infinty please i need some tips?
Edit
I read about cursors, but how can i use it
Your SQL seems fine and that is the preferred solution. A cursor will normally be far, far slower.
If you cannot create an index and the update above is really that slow, try the following. Considering I don't have the rest of the table definition to work with, I assume your primary key is a single field named ID:
First, create a temporary table with only the matching records:
CREATE TEMPORARY TABLE temp as
SELECT *
FROM Table_1
WHERE value = 'Some string without changes';
Then, update using this temporary table:
UPDATE Table_1 SET
Table_1.Value = 'Some string with but changed'
WHERE EXISTS (
SELECT *
FROM Temp
WHERE Temp.ID = Table_1.ID
);
Another approach if your DB is higher than 11g R1 version. Oracle has provided a beautiful package called DBMS_PARALLEL_EXECUTE which is used for large DMLS or any process which can be split into chunks and can be parallely done.

TRIGGER Oracle to prevent updating or inserting

I am having problems with this code below, which is a trigger used in Oracle SQL:
CREATE OR REPLACE TRIGGER TRG_TUTOR_BLOCK
BEFORE INSERT OR UPDATE ON tutors
FOR EACH ROW
DECLARE
BEGIN
IF :new.tutorName = :old.tutorName
THEN
RAISE_APPLICATION_ERROR(-20101, 'A tutor with the same name currently exists.');
ROLLBACK;
END IF;
END;
/
This trigger is used to prevent users from entering the same tutor name at different records.
After I insert two records with the same tutorname, the trigger does not block me from inserting it. Is there anyone can tell me what are the problems with this coding? Here are the sample format and insert values:
INSERT INTO tutors VALUES (tutorID, tutorName tutorPhone, tutorAddress, tutorRoom, loginID);
INSERT INTO tutors VALUES ('13SAS01273', 'Tian Wei Hao', '019-8611123','No91, Jalan Wangsa Mega 2, 53100 KL', 'A302', 'TianWH');
Trigger in Kamil's example will throw ORA-04091, you can see this with your own eyes here. ROLLBACK in a trigger is unnecessary, it runs implicitly when a trigger makes a statement to fail.
You can prohibit any DML on table by altering it with read only clause:
alter table tutors read only;
At last, integrity should be declarated with integrity constraints and not with triggers.
Good luck!
You don't need a trigger for this in Oracle.
You can do it with an "unique index" on the tutorName column (see http://docs.oracle.com/cd/B28359_01/server.111/b28310/indexes003.htm#i1106547).
Note: about your trigger, it fails on checking for another record with the same tutorName because it's not scanning the tutors table for another record with the same tutorName, it's just comparing the tutorName values of the row you are creating (in this case, old.tutorName is just NULL, because the row doesn't exist yet).
Check the case in yours trigger body
IF :new.tutorName = :old.tutorName
It returns true only if 'tutorName' value is the same in new and old record. When you'll trying to updat some value you'll get
IF 'someTutorName' = 'someTutorName'
which will return TRUE.
Inserting row cannot fire this rule because you're trying to compare something like that:
'someTutorName' = NULL
This case always returns FALSE.
Try to use something like that
CREATE OR REPLACE TRIGGER TRG_TUTOR_BLOCK
BEFORE INSERT OR UPDATE ON tutors
FOR EACH ROW
DECLARE
rowsCount INTEGER;
BEGIN
SELECT COUNT(*) FROM tutors WHERE tutorName is :new.tutorName INTO rowsCount;
IF rowsCount > 0
THEN
RAISE_APPLICATION_ERROR(-20101, 'A tutor with the same name currently exists.');
ROLLBACK;
END IF;
END;
/
But the best solution is the one mentioned by friol - use unique index by executing SQL like this
ALTER TABLE tutors
ADD CONSTRAINT UNIQUE_TUTOR_NAME UNIQUE (tutorName);
If you wanna completely ignore recording a row to a table you can follow these steps
rename table to something else and create a view with the same name and create an instead of trigger.
create table usermessages (id number(10) not null)
GO
alter table usermessages rename to xusermessages
GO
create or replace view usermessages as (select * from xusermessages)
GO
create or replace trigger usermessages_instead_of_trg
instead of insert or update on usermessages
for each row
begin
Null ;
end ;
GO
insert into usermessages(123)
Live test available here below
http://sqlfiddle.com/#!4/ad6bc/2

Insert default value when null is inserted

I have an Oracle database, and a table with several not null columns, all with default values.
I would like to use one insert statement for any data I want to insert, and don't bother to check if the values inserted are nulls or not.
Is there any way to fall back to default column value when null is inserted?
I have this code:
<?php
if (!empty($values['not_null_column_with_default_value'])) {
$insert = "
INSERT INTO schema.my_table
( pk_column, other_column, not_null_column_with_default_value)
VALUES
(:pk_column,:other_column,:not_null_column_with_default_value)
";
} else {
$insert = "
INSERT INTO schema.my_table
( pk_column, other_column)
VALUES
(:pk_column,:other_column)
";
}
So, I have to omit the column entirely, or I will have the error "trying insert null to not null column".
Of course I have multiple nullable columns, so the code create insert statement is very unreadable, ugly, and I just don't like it that way.
I would like to have one statement, something similar to:
INSERT INTO schema.my_table
( pk_column, other_column, not_null_column_with_default_value)
VALUES
(:pk_column,:other_column, NVL(:not_null_column_with_default_value, DEFAULT) );
That of course is a hypothetical query. Do you know any way I would achieve that goal with Oracle DBMS?
EDIT:
Thank you all for your answers. It seams that there is no "standard" way to achieve what I wanted to, so I accepted the IMO best answer: That I should stop being to smart and stick to just omitting the null values via automatically built statements.
Not exactly what I would like to see, but no better choice.
For those who reading it now:
In Oracle 12c there is new feature: DEFAULT ON NULL. For example:
CREATE TABLE tab1 (
col1 NUMBER DEFAULT 5,
col2 NUMBER DEFAULT ON NULL 7,
description VARCHAR2(30)
);
So when you try to INSERT null in col2, this will automatically be 7.
As explained in this AskTom thread, the DEFAULT keyword will only work as a stand-alone expression in a column insert and won't work when mixed with functions or expressions such as NVL.
In other words this is a valid query:
INSERT INTO schema.my_table
( pk_column, other_column, not_null_column_with_default_value)
VALUES
(:pk_column,:other_column, DEFAULT)
You could use a dynamic query with all rows and either a bind variable or the constant DEFAULT if the variable is null. This could be as simple as replacing the string :not_null_column_with_default_value with the string DEFAULT in your $insert.
You could also query the view ALL_TAB_COLUMNS and use nvl(:your_variable, :column_default). The default value is the column DATA_DEFAULT.
I think the cleanest way is to not mention them in your INSERT-statement. You could start writing triggers to fill default values but that's heavy armor for what you're aiming at.
Isn't it possible to restructure your application code a bit? In PHP, you could construct a clean INSERT-statement without messy if's, e.g. like this:
<?php
$insert['column_name1'] = 'column_value1';
$insert['column_name2'] = 'column_value2';
$insert['column_name3'] = '';
$insert['column_name4'] = 'column_value4';
// remove null values
foreach ($insert as $key => $value) {
if (is_null($value) || $value=="") {
unset($insert[$key]);
}
}
// construct insert statement
$statement = "insert into table (". implode(array_keys($insert), ',') .") values (:". implode(array_keys($insert), ',:') .")";
// call oci_parse
$stid = oci_parse($conn, $statement);
// bind parameters
foreach ($insert as $key => $value) {
oci_bind_by_name($stid, ":".$key, $value);
}
// execute!
oci_execute($stid);
?>
The better option for performance is the first one.
Anyway, as I understand, you don't want to repeat the insert column names and values due the difficult to make modifications. Another option you can use is to run an insert with returning clause followed by an update:
INSERT INTO schema.my_table
( pk_column, other_column, not_null_column_with_default_value)
VALUES
(:pk_column,:other_column, :not_null_column_with_default_value)
RETURNING not_null_column_with_default_value
INTO :insered_value
It seems to work with PHP.
After this you can check for null on insered_value bind variable. If it's null you can run the following update:
UPDATE my_table
SET not_null_column_with_default_value = DEFAULT
WHERE pk_column = :pk_column:
I would like to use one insert
statement for any data I want to
insert, and don't bother to check if
the values inserted are nulls or not.
Define your table with a default value for that column. For example:
create table myTable
(
created_date date default sysdate,
...
)
tablespace...
or alter an existing table:
alter table myTable modify(created_date default sysdate);
Now you (or anyone using myTable) don't have to worry about default values, as it should be. Just know that for this example, the column is still nullable, so someone could explicitly insert a null. If this isn't desired, make the column not null as well.
EDIT: Assuming the above is already done, you can use the DEFAULT keyword in your insert statement. I would avoid triggers for this.

Resources