Adding a unique key to the oracle database table - oracle

Trying to implement a friendship table ..
To explain wat i have done till now
my DDL
<!-- WORKING -- "relationship" - This table used to store the relationship between users -->
create table relationship(
relation_id number(8),
FromUserName varchar2(30),
ToUserName varchar2(30),
StatusId number,
SentTime timestamp,
constraint relationship_pk primary key(relation_id),
foreign key (FromUserName) references users(username),
foreign key (ToUserName) references users(username)
);
<!--WORKING add the unique key to 'relationship' table so that a user can send request at time to user only oncle -->
ALTER TABLE relationship
ADD CONSTRAINT relation_unique UNIQUE (FromUserName, ToUserName);
Here is an image to explain the problem
My problem
have a look at last two rows . .. the users kamlesh1 send request to jitu1 and again jitu1 sends request to kamlesh1 and when i kamlesh1 accepts the request the statusid changes to 1 similar case for kamlesh to jitu when jitu accepts the request.
I want to prevent this kind of duplication i.e
once a user has sent u a request u cannot sent a request to him just accept his request or reject it.
I just could'nt think of proper question title ...if u could help with that too.
Please help

You could create a unique function-based index for this:
CREATE UNIQUE INDEX relation_unique ON relationship ( LEAST(FromUserName, ToUserName), GREATEST(FromUserName, ToUserName) );
A couple of side notes: You don't need a NUMBER (38 digits of precision) to store a value that is either 0 or 1. NUMBER(1) should suffice. Also, you probably don't need the granularity of TIMESTAMP for SentTime - a DATE should do the trick, and might make arithmetic a bit easier (DATE arithmetic is expressed in days, TIMESTAMP arithmetic in intervals). Last, using CamelCase for column names in Oracle isn't a good idea since Oracle object names aren't case-sensitive unless you enclose them in double quotes. If you were to inspect the data dictionary you would see your columns like this: FROMUSERNAME, TOUSERNAME. Much better to use column names like FROM_USERNAME and TO_USERNAME (or USERNAME_FROM and USERNAME_TO).

You should order the persons. Say, add
alter table relationship
add constraint relation_order_chk
check (fromusername < tousername);
Then, when inserting, do something like
create or replace procedure AddRelationship(p_from varchar2, p_to varchar2 ...) is
begin
insert into relationship (fromusername, tousername, ...)
values(least(p_from, p_to), greatest(p_from, p_to), ...);
end;

Related

"Sequence generated" Is Not Shown to be in Order? Oracle forms

execute_query applied to show all records, sequence works fine but not in descending order
please help if there's any way to order this data block by the sequence in ("No", column)
create table Citizens_lic
(
No NUMBER(10) ,
ID NUMBER(10)
constraint Citizens_ID_pk_1 PRIMARY KEY,
F_Name VARCHAR2(32) ,
M_Name VARCHAR2(32) ,
L_Name VARCHAR2(32) ,
DOB DATE ,
POB VARCHAR2(32) ,
GENDER VARCHAR2(32) ,
WORK_STATUS VARCHAR2(32) ,
Soc_status VARCHAR2(32) ,
ISS_DATE date ,
EXP_Date date
)
this is the table, here is the sequence:
CREATE SEQUENCE CITIZENS_LIC_NO_SEQ_1
START WITH 1
INCREMENT BY 1
here is the trigger:
CREATE OR REPLACE TRIGGER CITIZENS_LIC_NO_TRIGG_1
BEFORE INSERT ON CITIZENS_LIC
FOR EACH ROW
BEGIN
SELECT CITIZENS_LIC_NO_SEQ_1.NEXTVAL
INTO :new.NO
FROM DUAL;
END;
try to add any value, it will work fine, but when you add values from the forms builder, it will change the order according to your mouse click
another matter is when I try to delete anything from the table,
the sequence ruined the order and the deleted value disappeared with its sequence number forever!
might forms' trigger help but I don't know which one is good to use
If you want to sort rows in a data block, open its properties palette, find ORDER BY property and put whatever you want in there. In your case, it seems that it would be
order by no desc
When you execute a query in that data block, the result will be sorted by the no column in descending order.
As of deleting rows: of course, it will be lost. What did you expect? Sequences guarantee unique, but not gapless list of numbers. Mind its cache; you don't even have to delete any rows, but two consecutive sessions might produce gaps. That's how sequences work; I'd just accept it, if I were you.
If you want to create gapless numbers, you'll have to write your own code and it won't be as simple as you think. You'll have to pay attention to inserts (which is simple), updates and deletes. Once again: stick to sequences.

Which strategy would be better related to the use of NEXTVAL and CURRVAL in Oracle?

I am developing a Online Registration System using JSP and Oracle where I need to give every successful registrant a unique registration number sequentially. For this I want to adopt the NEXTVAL facility of Oracle, but I am in a dilemma for which policy I would adopt.
Policy 1: First I will store the NEXTVAL of a sequence in the following way.
select seq_form.nextval slno from dual;
Then I will store the slno into a variable say
int slno;
Now I will use the slno for in the insert query when the user finally submits the form, like
insert into members(registration_no, name,...) values(slno, name, ...);
Here the registration_no is primary key.
Policy 2: In my second policy, I will run the insert the query first
insert into members(registration_no, name,...) values(seq_form.nextval, name, ...);
fetch the last inserted ID like
select seq_form.currval slno from dual;
And then store the same in some variable say
int slno;
And use the same to show it to the registrant. Now I can't come to a conclusion which is better in terms of safety and efficiency. Here, I must make it clear that, in both the cases, my intension is to give the user a unique sequential number after successful submission of the form and by safety I meant to say that the user should get the ID respect to his/her own web session. Please help me.
I suggest you do it slightly differently:
Create a BEFORE INSERT trigger on your MEMBERS table. Set REGISTRATION_NO column to SEQ_FORM.NEXTVAL in the trigger:
CREATE OR REPLACE TRIGGER MEMBERS_BI
BEFORE INSERT ON MEMBERS
FOR EACH ROW
BEGIN
:NEW.REGISTRATION_NO := SEQ_FORM.NEXTVAL;
END MEMBERS_BI;
Do NOT put REGISTRATION_NO into the column list in your INSERT statement - it will be set by the trigger so there's no need to supply any value for it:
Use the RETURNING clause as part of the INSERT statement to get back the value put into REGISTRATION_NO by the trigger:
INSERT INTO MEMBERS (NAME, ...)
VALUES ('Fred', ...)
RETURNING REGISTRATION_NO INTO some_parameter
If you are using oracle 12, you can use identity column.
Then use RETURNING to get auto-generated value back.
Go with the policy 2. Because you cant always be sure that the insert query will be successful. If the insert fails, your oracle sequence has been rolled forward and you lose a sequence.
it is a better idea to insert and then later fetch it into a variable.

Trigger - inserting default values into primary key when missing data

I have problem with creating triggers to table.
create table dwarfs (
name varchar2(20),
nickname varchar2(20),
note varchar2(20),
primary key (name,nickname)
);
Idea:
When someone want to insert data without entering name trigger should add default name for example "Dwarf1"
I created trigger but I get
communicate:SQL Error: ORA-01400: cannot insert NULL into
01400. 00000 - "cannot insert NULL into (%s)"
create or replace trigger t_d
before insert or update on dwarfs
for each row
when (new.name=null or new.name= '')
declare
begin
:new.name:='Dwarf1';
end;
As #kodirko noted in his comment, the comparison (new.name=null or new.name='') will never work, because a comparison with NULL always returns NULL, not TRUE or FALSE. To determine if a column is NULL you need to use the special comparison construct IS NULL. Also note that because nickname is part of the primary key it also must never be NULL - so, when taken all together you might try rewriting your trigger as:
create or replace trigger t_d
before insert or update on dwarfs
for each row
when (new.name IS NULL OR NEW.NICKNAME IS NULL)
begin
IF :new.NAME IS NULL THEN
:new.NAME := 'Dwarf1';
END IF;
IF :new.NICKNAME IS NULL THEN
:new.NICKNAME := :new.NAME;
END IF;
end;
Share and enjoy.
This is a typical case of not seeing the forest for the trees. Yes, there is the technical detail of using = in a test for null. The main point, however is...
NEVER, EVER ASSIGN DEFAULT VALUES TO KEY FIELDS!!!
If a field is a key field, the tuple is explicitly unusable without it. If that data is missing, there is something very, very wrong and the remainder of the row should be prevented at all costs from being inserted into the database.
This does not, of course, apply to an Identity or auto generating value that is defined as the surrogate key. Surrogate keys are, by definition, completely independent of the entity data. (This points out a disadvantage of surrogate keys, but that is a different discussion.) This applies only to attribute fields that have been further identified as key fields.
If the value is missing and a default value is not supplied, any attempt to insert the row will generate an error. Which is exactly what you want to happen. Don't make it easy for the users to destroy the integrity of the database.

DB2 duplicate key error when inserting, BUT working after select count(*)

I have a - for me unknown - issue and I don't know what's the logic/cause behind it. When I try to insert a record in a table I get a DB2 error saying:
[SQL0803] Duplicate key value specified: A unique index or unique constraint *N in *N
exists over one or more columns of table TABLEXXX in SCHEMAYYY. The operation cannot
be performed because one or more values would have produced a duplicate key in
the unique index or constraint.
Which is a quite clear message to me. But actually there would be no duplicate key if I inserted my new record seeing what records are already in there. When I do a SELECT COUNT(*) from SCHEMAYYY.TABLEXXX and then try to insert the record it works flawlessly.
How can it be that when performing the SELECT COUNT(*) I can suddenly insert the records? Is there some sort of index associated with it which might give issues because it is out of sync? I didn't design the data model, so I don't have deep knowledge of the system yet.
The original DB2 SQL is:
-- Generate SQL
-- Version: V6R1M0 080215
-- Generated on: 19/12/12 10:28:39
-- Relational Database: S656C89D
-- Standards Option: DB2 for i
CREATE TABLE TZVDB.PRODUCTCOSTS (
ID INTEGER GENERATED BY DEFAULT AS IDENTITY (
START WITH 1 INCREMENT BY 1
MINVALUE 1 MAXVALUE 2147483647
NO CYCLE NO ORDER
CACHE 20 )
,
PRODUCT_ID INTEGER DEFAULT NULL ,
STARTPRICE DECIMAL(7, 2) DEFAULT NULL ,
FROMDATE TIMESTAMP DEFAULT NULL ,
TILLDATE TIMESTAMP DEFAULT NULL ,
CONSTRAINT TZVDB.PRODUCTCOSTS_PK PRIMARY KEY( ID ) ) ;
ALTER TABLE TZVDB.PRODUCTCOSTS
ADD CONSTRAINT TZVDB.PRODCSTS_PRDCT_FK
FOREIGN KEY( PRODUCT_ID )
REFERENCES TZVDB.PRODUCT ( ID )
ON DELETE RESTRICT
ON UPDATE NO ACTION;
I'd like to see the statements...but since this question is a year old...I won't old my breath.
I'm thinking the problem may be the
GENERATED BY DEFAULT
And instead of passing NULL for the identity column, you're accidentally passing zero or some other duplicate value the first time around.
Either always pass NULL, pass a non-duplicate value or switch to GENERATED ALWAYS
Look at preceding messages in the joblog for specifics as to what caused this. I don't understand how the INSERT can suddenly work after the COUNT(*). Please let us know what you find.
Since it shows *N (ie n/a) as the name of the index or constraing, this suggests to me that is is not a standard DB2 object, and therefore may be a "logical file" [LF] defined with DDS rather than SQL, with a key structure different than what you were doing your COUNT(*) on.
Your shop may have better tools do view keys on dependent files, but the method below will work anywhere.
If your table might not be the actual "physical file", check this using Display File Description, DSPFD TZVDB.PRODUCTCOSTS, in a 5250 ("green screen") session.
Use the Display Database Relations command, DSPDBR TZVDB.PRODUCTCOSTS, to find what files are defined over your table. You can then DSPFD on each of these files to see the definition of the index key. Also check there that each of these indexes is maintained *IMMED, rather than *REBUILD or *DELAY. (A wild longshot guess as to a remotely possible cause of your strange anomaly.)
You will find the DB2 for i message finder here in the IBM i 7.1 Information Center or other releases
Is it a paging issue? we seem to get -0803 on inserts occasionally when a row is being held for update and it locks a page that probably contains the index that is needed for the insert? This is only a guess but it appears to me that is what is happening.
I know it is an old topic, but this is what Google shown me on the first place.
I had the same issue yesterday, causing me a lot of headache. I did the same as above, checked the table definitions, keys, existing items...
Then I found out the problem was with my INSERT statement. It was trying to insert to identical records at once, but as the constraint prevented the commit, I could not find anything in the database.
Advice: review your INSERT statement carefully! :)

Is it possible to configure Oracle session/connection to automatically uppercase strings?

Does anyone know if it's possible to configure an Oracle session or connection so that every string that gets persisted is automatically uppercased?
For example, if I invoke a SQL like this: "INSERT INTO STUDENT (name) VALUES ('john doe')"
The information in my table would be persisted like this:
STUDENT
--------------------
ID | 1
NAME | JOHN DOE
I've checked this entry but couldn't find anything like this: http://docs.oracle.com/cd/B19306_01/server.102/b14225/ch3globenv.htm#sthref186
Thanks!
There is no session-level configuration parameter for that, no.
You could write a trigger on the STUDENT table that would automatically store the data in uppercase but you'd need to do that for every table.
CREATE TRIGGER trg_student
BEFORE INSERT ON student
FOR EACH ROW
BEGIN
:new.name := upper( :new.name );
END;
Depending on the problem you are trying to solve, you could potentially set your session's NLS settings to ignore case sensitivity so that the string 'John Doeis considered to be equal to the stringJOHN DOE`. The options, limitations, and downsides to this will vary with the specific version of Oracle.

Resources