Oracle Partial Foreign Key that is not unique - oracle

I have some problems with Oracle Foreign Composite keys. I have an application that is somewhat big (you know, 5000+ tables, that kind of thing) and we store some 'static' (it actually can change) data into some set of tables. This data is referenced by a lot of the tables through the database, so it worked like this:
TABLE StaticData
ID(PK) Data
1 StaticData1
2 StaticData2
...
n StaticDataN
TABLE TypicalTable
ID(PK) StaticDataID(FK to StaticData)
1 1
2 1
3 7
4 2
...
n n
And all was well in Wonderland.
But some changes of spec, and some meetings with the client after, we were tasked with having different 'versions' of the data ready to replace the static data when some time arrives. Last part was easy, we can create jobs that will check every day/week for a date and change the data, but we will have to maintain older and newer versions of the data... in the same table. So now StaticData looks like:
TABLE StaticData
ID(PK) Data KickInDate(Also PK)
1 StaticData1.1 01/01/1900
1 StaticData1.2 10/07/2014
1 StaticData1.3 12/12/2015
2 StaticData2.1 01/01/1900
...
n StaticDataN.1 01/01/1900
And of course all integrity reference has gone off the board. And of course, since I cannot put a UNIQUE constraint in the ID, I cannot keep the foreign keys.
I have searched the net for a solution for this (less restrictive kind of foreign keys) and most of the time the solution is to use triggers checking BEFORE INSERT|UPDATE|DELETE
But that will be kind of a very, very, very big job.
So I ask, Do I have other solutions?
Is there any way to tell Oracle to reference another column of another table even thought is not UNIQUE? (it will definitely be NOT NULL).

Primary keys in Oracle can have duplicates. Primary keys can be built with non-unique indexes and existing values can be excluded by creating the constraint with NOVALIDATE. It is a rarely used feature, will confuse people, and is not a clean solution. But in the real world sometimes data isn't clean and there's no time for the perfect solution.
Sample schema and data.
create table staticData
(
id number not null,
data varchar2(100),
constraint staticData_pk primary key (id)
);
create table typicalTable
(
id number not null,
staticDataID number,
constraint typicalTable_pk primary key (id),
constraint typicalTable_fk foreign key (staticDataID)
references staticData(id)
);
insert into staticData values (1, 'StaticData1');
insert into staticData values (2, 'StaticData2');
insert into typicalTable values(1, 1);
insert into typicalTable values(2, 1);
Process to drop constraints, add duplicate data, and re-enable constraints.
--Drop constraints.
alter table typicalTable drop constraint typicalTable_fk;
alter table staticData drop constraint staticData_pk;
--Add semi-duplicate data.
insert into staticData values (1, 'StaticData1.2');
--Use a non-unique index to build a NOVALIDATE primary key.
create index staticData_pk on staticData(id);
alter table staticData add constraint staticData_pk primary key (id) novalidate;
alter table typicalTable add constraint typicalTable_fk foreign key(staticDataID)
references staticData(id);

No, the target column(s) must be unique, that's the whole idea. However, you can propagate an additional version column from StaticData to TypicalTable:
CREATE TABLE StaticData (
id NUMBER,
version NUMBER,
col1 ... coln,
PRIMARY KEY (id,version)
);
CREATE TABLE TypicalTable (
StaticDataID NUMBER,
version NUMBER,
colx ... coly,
FOREIGN KEY (StaticDataID, version) REFERENCES StaticData(id, version)
);

Related

In which order should we apply primary key, foreign key constraints and create index if the Oracle table has data?

1.In which order should we apply primary key, foreign key constraints and create index if the Oracle table has millions of data and does not have prior constraints?
2.Can we use 'NOLOGGING PARALLEL' while applying primary key and foreign key constraints like we do while applying(creating) indexes? Or any other method so that primary key and foreign key constraints could be applied faster?
Note: I'll use bullets so that it is easier to read, as it is easy to get lost in long sentences.
My thoughts on the subject; see if anything of this helps.
Well,
as you can't create a foreign key constraint if column(s) it references aren't part of primary or unique key
you'll obviously first have to create primary key constraints
and then foreign key constraints
When you
create a primary key constraint,
Oracle automatically creates index that supports it, unless there's already an index you can use (with the USING INDEX clause)
which means that you can "skip" some indexes (those for primary key constraints as they'll already exist) and virtually save some time
and create "other" indexes
On the other hand,
if you first create unique index on future primary key columns and
later add primary key constraint with the USING INDEX clause, Oracle will "skip" check for possible duplicate values because unique index won't allow them
The same goes for
NOT NULL constraint on future primary key columns; primary key doesn't allow NULLs so - if a column already is NOT NULL, enforcing primary key constraint can skip NULL check as well
I don't know
which columns you'll additionally index, but - as you're on Oracle 11g -
don't forget to index all foreign key constraint columns
because you might encounter unexpected table locks if you
update primary key column in parent table, or
delete parent record
Can you do it with no logging and in parallel? Yes:
SQL> create table test (id number, name varchar2(20));
Table created.
SQL> create unique index ui1_test_id on test (id) nologging parallel 20;
Index created.
SQL> alter table test add constraint pk_test primary key (id) using index ui1_test_id nologging parallel 20;
Table altered.
SQL>
But you'll probably want to change the index and table back to NOPARALLEL and LOGGING when the initial creation is done.
SQL> alter index ui1_test_id noparallel;
Index altered.
SQL> alter table test logging noparallel;
Table altered.

how to add sequence to primary key in plSQL

I'm using pl sql developer 12.what i want is to add the make the primary key sequential using oracle 12' windows and forms , not by scripting. I can't find out how?.
aslo, how can i make one to one relation between 2 tables (user,role), in user table i added role_id as foreign key; but the relation seem one to many!!!
In Oracle 12c and above, you define that column as identity columns:
CREATE TABLE mytable (
id NUMBER GENERATED ALWAYS AS IDENTITY,
-- other columns ...
);
A #Mureinik already said, in Oracle 12 and higher versions you can define your primary key column as NUMBER GENERATED ALWAYS AS IDENTITY to get a unique sequential value.
To enforce a one-to-one relationship in the database you'd want to have your ROLE_ID column defined as a primary key on one table, and as either a UNIQUE or PRIMARY key on the second table, with a foreign key relationship between the tables. In other words:
CREATE TABLE T1 (ROLE_ID NUMBER
CONSTRAINT PK_T1
PRIMARY KEY
...);
CREATE TABLE T2 (ID_T2 NUMBER
PRIMARY KEY,
ROLE_ID NUMBER
CONSTRAINT T2_U1
UNIQUE
CONSTRAINT T2_FK1
REFERENCES T1(ROLE_ID)
ON DELETE CASCADE,
...);
After the above ROLE_ID will be the primary key on T1, a unique key on T2, and T2.ROLE_ID will be a foreign key to T1.
Best of luck.
what I have done lastly is that i downloaded sql developer 64 w, and from it; I connected with the database then I made the column sequential

How i can solve ORACLE problem in foreign key

I have a problem in oracle and I need help. I have the following query:
1 CREATE TABLE TEST1 (
2 NAME VARCHAR(20)
3 ID VAR(9)
4 PRIMARY KEY(ID)
5 FOREIGN KEY(NAME) References TEST2(ANAME)
6 ON DELETE CASCADE ON UPDATE SET NULL );
If I want to delete line #6 what should i do?
"How I can change the value of primary key and based of that the foreign keys of this pk will change too?"
First, you should never need to do that. Primary keys like this are really just numbers that identify a row, they have no meaning in themselves. It's like asking how you would change the ROWID of a row.
If you must, you could:
Find the foreign keys pointing to this table and disable them with ALTER CONSTRAINT myconstraint DISABLE
Update your primary table and catch the new id value with UPDATE test1 SET id = mysequence.NEXTVAL WHERE id = :oldid RETURNING id INTO :newid, assuming it's set by a sequence.
Update the ids in your other tables with the new id.
Reenable your constraints.
Note that altering constraints is DDL and will do an implicit commit and this approach will leave your tables unprotected by the foreign key constraints.
A second approach would be to:
Insert a new row in the primary table and catch the new id.
Update the id in the foreign tables with the new id.
Delete the old row in the primary table.
Now that I think about it, that second approach seems better to me. No DDL and it just seems cleaner.

FK on a single column referencing a column from composite PK

Not able to create /find the logic to apply FK on a column in child table referencing a column from composite PK of parent table.
create table product(prod_id number,
prod_name varchar2(20),
price number,
constraint PK12 primary key(prod_id,prod_name));
Table created.
create table purchase(prod_id number,
purchase_price number,
constraint FK12 foreign key(prod_id) references product(prod_id));
create table purchase(prod_id number,
purchase_price number,
constraint FK12 foreign key(prod_id) references product(prod_id))
ERROR at line 1:
ORA-02270: no matching unique or primary key for this column-list
Kinldy suggest how i can incorporate this logic.
Thanks.
You can't.
As the error says there's no matching primary key for that column list; you must have one. You have three options:
Remove PROD_NAME from the primary key of PRODUCT. On the face of it this seems like the logical solution, if this is not required in order to make the primary key unique.
Add PROD_NAME to the PURCHASE table.
Create a unique index on PURCHASE.PROD_ID. This seems excessive if it would be a primary key candidate anyway.
I suspect that this is not unique to Oracle. Considering you have a composite primary key in the referenced table, that implies that only one of the columns comprising the composite key is not enough to uniquely identify the record in that table. Therefore, it's impossible to reference only a single column of the primary key in a foreign key relationship that's one-to-many (e.g. one record in the referenced table can have many records in the referencing table--the one with the FK). However, if the relationship to be established is many-to-many, this may be possible.
HTH.

does foreign key always reference to a unique key in another table?

Is it not possible that foreign key(single column) in a child table references to a parent key which has some duplicate values?
By the SQL standard, a foreign key must reference either the primary key or a unique key of the parent table. If the primary key has multiple columns, the foreign key must have the same number and order of columns. Therefore the foreign key references a unique row in the parent table; there can be no duplicates.
Re your comment:
If T.A is a primary key, then no you can't have any duplicates. Any primary key must be unique and non-null. Therefore if the child table has a foreign key referencing the parent's primary key, it must match a non-null, unique value, and therefore references exactly one row in the parent table. In this case you can't make a child row that references multiple parent rows.
You can create a child row whose foreign key column is NULL, in which case it references no row in the parent table.
No, it is not possible.
When you define a foreign key constraint on a table, it means there is only one corresponding key on the foreign table. If multiples existed on the foreign table which one would be meant?
Wikipedia has this definition on the Foreign key entry:
A foreign key is a field in a relational table that matches a candidate key of another table
Candidate keys are unique within a table.
Yes, it is possible for a foreign key to reference a column with duplicate values.
This can happen if the primary key uses a non-unique index and is not validated when it is created. (But I have never seen a situation like this in real life. As #Bill Karwin pointed out, it would be very confusing. So this may not be a situation you really need to worry about.)
--Create a table with two duplicate rows
create table test1(a number);
insert into test1 values(1);
insert into test1 values(1);
commit;
--Create a non-unique index
create index test1_index on test1(a);
--Use the non-unique index for the primary key, do not validate
alter table test1 add constraint test1_pk primary key (a)
using index test1_index novalidate;
--Build another table with a foreign key to TABLE1
create table test2(a number,
constraint test2_fk foreign key (a) references test1(a));
--Inserting a value that refers to the duplicate value still works.
insert into test2 values(1);
commit;
--The foreign key still works:
--ORA-02291: integrity constraint (TEST2_FK) violated - parent key not found
insert into test2 values(2);
--The primary key works as expected, but only for new values:
--ORA-00001: unique constraint (TEST1_PK) violated
insert into test1 values(1);

Resources