How to make sure random key generated in trigger complies to unique constraint - oracle

Happy new year! :)
I have a table and upon insert I fill the column random_key with a random string using a trigger.
This column has a unique constraint, but theoretically it is possible that the random string the trigger generates is not unique, and the record will not be inserted.
Is there a way to avoid this in the trigger? Or do I have to create an insert procedure?
edit: (copied from comments and elaborated)
I have a normal id in this table, but for external use (the internet) I require a completely random id, so that the id of the next row in the table cannot be guessed.
Salting with the id + constant might work. I'll think it over.
Judging by the answers so far it appears there is no simple solution to avoid duplication in the trigger. That is an answer too. :)

you can use the SYS_GUID function. this will return a unique string.
SQL> select sys_guid() from dual;
SYS_GUID()
--------------------------------
D24FC8257402951BE0401AA2C9997E18

Related

TRIGGER in oracle to prevent insert duplicate data

I'm learning Oracle and I had a problem. I have my "Chat" table:
CREATE TABLE chat (
id_chat NUMBER,
id_user NUMBER,
start_chat DATE,
end_chat DATE
);
Now, I created a trigger so that it does not allow me to enter a "Chat" if an old one was already registered with the same id_user. This is my trigger:
create or replace trigger distChat
before insert on Chat
for each row
begin
if :new.id_user = :old.id_user then
Raise_Application_Error(-20099,'YOU CAN'T INSERT DUPLICATED DATA.');
end if;
end distChat;
But still it still allows me to enter Chat with the same user code. Any help or recommendation to improve my trigger that does not work.
Thank you.
Don't use a TRIGGER.
Two reasons:
Oracle provides a mechanism for preventing duplicates
Triggers are expensive, and another database object to maintain
Do this
ALTER table CHAT ADD CONSTRAINT xpk_chat PRIMARY KEY ( ID_CHAT );
I don't know your data model, but I think you want ID_CHAT to distinguish a chat. If you do this for ID_USER, then a user couldn't ever have more than one chat...and who would want to use that system? If I'm wrong, just change the column referenced in the ALTER command above.
Now your table will have a constraint that prevents duplicate values on ID_CHAT column. This is called a PRIMARY KEY (Docs)
Additionally, you will have an INDEX, so querying your CHAT's by their ID value could be quicker.
P.S. Your Trigger isn't doing what you want it to do. If you were to do it with a trigger you would need to raise the exception if :new.id_user in (select distinct id_user from chat)...so basically if the ID resulting in the INSERT was already found in the table, there would be an exception. The beauty of the PK constraint is, that the database does this FOR YOU.

How to generate PHYSICALLY sorted GUID as primary key in Oracle?

I'm trying to generate a sorted GUID as a primary key in Oracle. In SQL Server I could use one of the following to sort the rows physically
By clustered primary key as a unique identifier.
By NEWSEQUENTIALID.
I have searched for an Oracle equivalent but failed to find a solution. I know about Is there a way to create an auto-incrementing Guid Primary Key in an Oracle database?, but there's no indication whether SYS_GUID() is sorted.
How could I create a sequential primary key in Oracle?
If you want to create a GUID then SYS_GUID() is what you should be using, you can create this in a table as per the linked question. It's unclear from the documentation whether SYS_GUID() is incrementing. It might be but that's not really a statement that imparts trust.
The next part of your question (and some comments) keeps asking about clustered primary keys. This concept does not exist in the same way in Oracle as it does in SQL Server and Sybase. Oracle does have indexed organized tables (IOT) ...
... a table stored in a variation of a B-tree index structure... rows
are stored in an index defined on the primary key for the table. Each
index entry in the B-tree also stores the non-key column values. Thus,
the index is the data, and the data is the index.
There are plenty of uses for IOTs but it's worth bearing in mind you're altering the physical structure of the database on the disk for "performance reasons". You're doing the ultimate of all premature optimizations using something that has both negative and positive aspects. Read the documentation and be sure that this is what you want to do.
I would generally use an IOT only when you don't care about DML performance but when you do a lot of range scans, or you need to order by the primary key. You create an IOT in the same way as you would an ordinary table, but because everything you want is now part of the table everything goes in your table definition:
create table test_table (
id raw(32) default sys_guid()
, a_col varchar2(50)
, constraint pk_my_iot primary key (id)
) organization index;
It's worth noting that even with an IOT you must use an explicit ORDER BY in order to guarantee returned order. However, because of the way this is stored Oracle can table a few short cuts:
select *
from ( select *
from test_table
order by id )
where rownum < 2
SQL Fiddle.
As with everything, test, don't assume that this is the structure you want.
Oracle has as SYS_GUID() function, which generates a 16-byte RAW datatype. But, I'm not sure what you mean by "sorted GUID". Can you elaborate?
Do you mean you need each generated GUID to sort "after" the previously generated GUID? I looked at the SYS_GUID() function, and it seems to generate GUIDs in sorted order, but looking at the documentation, I don't see anything that says that is guaranteed.
If I understand your question correctly, I'm not sure it's possible.
You may be able to use SYS_GUID() and prepend a sequence, to get your desired sort order?
Can you explain more about your use case?
Adding the following in response to comment:
Ok, now I think I understand. What I think you want, is something called an IOT, or Index Organized Table, in Oracle. It's a table that has an index strucure, and all data is clustered, or grouped by the primary key. More information is available here:
http://docs.oracle.com/cd/E16655_01/server.121/e17633/indexiot.htm#CNCPT721
I think that should do what you want.

Conditional Insert or Update in Oracle

I have one table in oracle where data gets inserted from some third party. I want to populate master tables from that table. So, what will be the best way performance wise using collection.
E.g. Suppose, the table into which data will get populated from third party is 'EMP_TMP'.
Now I want to populate 'EMPLOYEE' master table through procedure which will get populated from EMP_TMP Table.
Here again there is one condition like IF SAME EMPID (this is not primary key) EXISTS then we have to UPDATE FULL TABLE which consists of SAME EMPID ELSE we have INSERT NEW RECORD.
[Note: Here EMPID is VARCHAR2 and EMPNO will be primary key where we will use SEQUENCE]
I think here merge will not perform much better performancewise since we cant use collection in MERGE statement.
Well, if performance is your primary consideration, and you don't like MERGE, then how about this (run as script, single transaction):
delete from EMPLOYEE where emp_id IN (
select emp_id from EMP_TMP);
insert into EMPLOYEE
select * from EMP_TMP;
commit;
Obviously not the "safest" approach (and as written assumes exact same table definitions and you have the rollback), but should be fast (you could also mess with IN vs EXISTS etc). And I couldn't quite understand your post if emp_id or emp_no was the common key in these 2 tables, but use whichever makes sense in your situation.
Create a procedure, you need to be using PL/SQL.
Do an update first then test sql%rowcount.
If it is 0, no updates where done and you have to do an insert instead.
I think that this is fairly efficient.
pseudo code
Update table;
if sql%rowcount = 0 then
//get new sequence number
insert into table;
END IF;
COMMIT;
HTH
Harv

Using JPA/Oracle can I have a unique constraint that ignores string case?

I have a db table with a column that is a String. I do not consider the case to be significant (e.g. "TEST == "test"). Unfortunately, it appears that JPA2 does, because both values are inserted into my table; I would like the second one to be rejected.
Is there a generic way to annotate an "ignore-case" unique constraint on a string column?
As an alternative, I could also consider putting a unique "ignore-case" constraint on the actual db column. Is that possible in Oracle 10?
What I don't want to do is write code, because this occurs often in this particular db.
All help is greatly appreciated.
you can achieve this with a function-based unique index
create unique index <index_name> on <table_name> (UPPER(<column_name>));
for Example
create table t111( col varchar2(10));
create unique index test_idx on t111 (UPPER(col));
insert into t111 values('test');
insert into t111 values ('TEST');

How to constrain a database table so only one row can have a particular value in a column?

Using Oracle, if a column value can be 'YES' or 'NO' is it possible to constrain a table so that only one row can have a 'YES' value?
I would rather redesign the table structure but this is not possible.
[UDPATE] Sadly, null values are not allowed in this table.
Use a function-based index:
create unique index only_one_yes on mytable
(case when col='YES' then 'YES' end);
Oracle only indexes keys that are not completely null, and the CASE expression here ensures that all the 'NO' values are changed to nulls and so not indexed.
This is a kludgy hack, but if the column allows NULLs, then you could use NULL in place of "NO" and use "YES" just as before. Apply a unique key constraint to that column, and you'll never get two "YES" values, but still have many NOs.
Update: #Nick Pierpoint: suggested adding a check constraint so that the column values are restricted to just "YES" and NULL. The syntax is all worked out in his answer.
You will want to check a Tom Kyte article with exactly this question being asked and his answer:
http://tkyte.blogspot.com/2008/05/another-of-day.html
Summary: don't use triggers, don't use autonomous transactions, use two tables.
If you use an Oracle database, then you MUST get to know AskTom and get his books.
It doesn't work on the table definition.
However, if you update the table using a trigger calling a stored procedure, you could make sure that only one row contains "YES".
Set all rows to "NO"
Set the row you want to YES
Following on from my comment to a previous answer by yukondude, I'd add a unique index and a check constraint:
create table mytest (
yesorno varchar2(3 char)
);
create unique index uk_mytest_yesorno on mytest(yesorno);
alter table mytest add constraint ck_mytest_yesorno check (yesorno is null or yesorno = 'YES');
Does Oracle support something like filtered indices (last week I heard that e.g. MSSQL2008 does)? Maybe you can define a unique key which applies only to rows with the value "Yes" in your column.
I guess I'd use a second table to point to the appropriate row in your current table. That other table could be used to store values of other variables too too.

Resources