Oracle constraint within a sequence - oracle

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;

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 11g - Is it possible to determine update or insert in a function / procedure?

I am working with Oracle 11g and I got a question.
What I want to do is :
make a procedure
when this procedure has called, it will move data form one table to the other (column is almost same, but has different primary key)
Since, they do not use same field as PK, it occurs an error if use insert statement only.
So I want to do if table has a key than update, otherwise insert.
i.e. DDL like follow. It has almost same, but pk.
create table Tbl_A (
a_pk number constraints pk_tbl_a primary key
, b_pk number
, some_text varchar2(10)
, created date
, changed date
);
create table Tbl_B (
a_pk number
, b_pk number constraints pk_tbl_b primary key
, some_text varchar2(10)
, created date
, changed date
);
Psuedo of what I want :
create or replace procedure mv_data
is
begin
case when [if Tbl_B has same b_pk] then [update statement] end
else [create statement] end;
commit;
end;
I know I can not use case when like above, but that what I am trying to achieve is something like that. MyBatis could be a solution, but the client want to this with only DB.(Actually, this job will be executed by Oracle DBMS_SCHEDULE)
Thanks for your kind answers :D
As an alternative to the MERGE solution as suggested by valentin you can use the following construction:
begin
-- try the insert
insert...
exception
when dup_val_on_index
then
update...
end;
You want to use MERGE
Some references:
https://oracle-base.com/articles/9i/merge-statement
http://psoug.org/reference/merge.html

Change from Number to Generated in PLSQL

I am trying to alter a table where the original datatype of this column is Number to a generated column, but I get an error of "ORA-00905 Missing Keyword"
Alter Table MyTable
Modify Column FlagColumn NUMERIC (38,0) GENERATED ALWAYS AS (CASE WHEN ValueColumn IS NULL THEN 1 ELSE 0 END) VIRTUAL;
Is my syntax correct?
Do I have any other options besides dropping and recreating the table?
The Oracle documentation pretty clearly doesn't support the syntax you're attempting. The obvious solution is to drop the column, then replace it:
ALTER TABLE mytable
DROP COLUMN flagcolumn;
ALTER TABLE mytable
ADD numeric GENERATED ALWAYS AS (CASE WHEN valuecolumn IS NULL THEN 1 ELSE 0 END) VIRTUAL;
There's really no reason not to do this, since you're getting rid of the column's original data in any case.

Is there a way similar check constraint to help me to know if there is a duplicate column

table 1
ID - name - main_number - random1 - random2
1* -aaaa-blalablabla*- *** - *
2 -vvvv-blublubluuu*- *** - *
3 -aaaa-blalablabla*- *** - **
ID , name and main number are primary key
My problem that I have noticed coulmn name and main number has duplicate values, i dont want to ADD ANY OTHER DUPLICATE VALUES ( I should keep the old duplicat because in my real table there are a lot of duplicated data and its hard to remove them )
what I want when I TRY ( BEFORE TO COMMIT) to know that this name I am trying to insert is duplicate.
I can do that with in a procedure or triger, but i have heard constraint checking is simpler and easier(if there a simpler way then procedure or triger ill be glad to learn it)
CONSTRAINT check_name
CHECK (name = (A_name))
can the constaraint have more then 1 column in such way?
CONSTRAINT check_name
CHECK (name = (A_name) , main_number=( A_number))
can I a write a constaraint in such way?
CONSTRAINT check_name
CHECK (name = ( select case where there is an column has the same value of column name))
So my question : Is there a way simelar to check constraint to help me to know if there is a duplicate column or I have to use a trigger ?
Since your database is Oracle you could also use NOVALIDATE constraints. Meaning: "doesn't matter how the data is, just validate from now on".
create table tb1
(field1 number);
insert into tb1 values (1);
insert into tb1 values (1);
insert into tb1 values (1);
insert into tb1 values (2);
insert into tb1 values (2);
commit;
-- There should be an non-unique index first
create index idx_t1 on tb1 (field1);
alter table tb1 add constraint pk_t1 primary key(field1) novalidate;
-- If you try to insert another 1 or 2 you would get an error
insert into tb1 values (1);
Yes, you can use constraints on many columns.
But in this case constraint is not applicable, because all table rows must satisfy constraints. Use a trigger.
Constraints cannot contain subqueries.
Alternatively use unique index, that will enforce unique constraint
create unique index index1 on table1
(case when ID <= XXX then null else ID end,
case when ID <= XXX then null else name end);
Replace 'XXX' with your current max(ID).
I assume that you want to prevent duplicate records as defined by the combination of name and main_number.
Then the way to go is to cleanup your database, and create a unique index:
create unique index <index_name> on <table> (name, main_number)
This both checks, and speed's it up.
In theory, if you really wanted to keep the old duplicate records, you could get along by using a trigger, but then you will have a hard time trying to get sense out of this data.
Update
If you used the trigger, you would end up with two partitions of data in one table - one is checked, the other is not. So all of your queries must pay attention to it. You just delay your problem.
So either clean it up (by deleting or merging) or move the old data in a separate table.
You can use SQL select ... group by to find your duplicates, so you can delete/move them in one turn.

Auto-increment in Oracle without using a trigger

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

Resources