How to add Oracle constraint to below tables - oracle

I have three tables:
A {op_id, op_name, .}
B {b_id, op_id, supplier_id, .}
C {c_id, op_id, op_id2, supplier_id, relation, .}
Table B and C have op_id which is foreign key from table A. In table A op_id is primary key, in table B b_id is primary key and in table C c_id is primary key. In table B supplier_id and op_id may have duplicate records. Now I want to add constraint so that if I delete records from Table B for op_id and if a relationship record exists for op_id in table C then it should not allow me to delete. Is it possible through constraint?

Add the constraints normally as specified by you like below
Table A op_id is primary key,
in table B b_id is primary key
and in table C c_id is primary key
Table B and C have op_id which is foreign key from table A
For the last condition that you have specified write a trigger before delete on table B and check if the op_id is present in the table C or not - if yes throw an error else allow it.
Hope this helps.

Most of your key constraints are fairly trivial, the difficult bit is the last part, ensuring that you cannot remove rows from B without first removing them from C.
One approach that might work here would be to create a fast-refreshable "on commit" materialised view which returns a count of the number of unique OP_IDs that exist in C but that are not in B. You could then add a check constraint to the view which enforces that the count must always be zero. This constraint would be enforced at the point of commit and would prevent you deleting from B without first deleting from C (deleting from B whilst matching records exist in C would violate the check constraint as the count in the materialized view would be greater than 0). A side effect of this would be that you wouldn't be able to insert into C without first inserting into B.

Related

Oracle: create a partition by reference (partition by reference(fk)) on non referenced table

We have realized that we need to use Oracle (12 c) interval partitioning.We have an hierarchical entity model with lots of #OneToMany relationships.We want to use "partitioning by range" (day,month... ) on the "parent/root" entity (A) and "partition by reference" on all child entities (B).
From the Oracle documentation: "Reference partitioning allows the partitioning of two tables related to one another by referential constraints. The partitioning key is resolved through an existing parent-child relationship, enforced by enabled and active primary key and foreign key constraints." The problem is that child entities (B) can refer to other entities ( C) that they don't have any link with the "parent/root" entity (A). I can create partitions on A and on B but when I want to drop the partition on A ( partition on B on cascade), I get an error integrity error and it fails. it works only if I delete all records on C and B and then partition them. I don't want to do that as it's not efficient and slow compared to dropping partitions directly
Please is there a way to create a partition on table C based on A(creation_date) without adding any foreign constraint between A and C?
Small example to illustrate the case
A - parent entity
B - child entity to A
C - child entity to B
create table
A (
id number primary key,
creation_date date
)
partition by range (creation_date)
INTERVAL(NUMTOYMINTERVAL(1, 'MONTH'))
(
partition p1 values less than (to_date('20180501','yyyymmdd'))
);
create table
B (
id number primary key,
value varchar2(5),
a_id number not null,
constraint fk_ba foreign key (a_id) references A
)
partition by reference(fk_ba);
create table
C (
id number primary key,
code varchar2(5),
b_id number not null,
constraint fk_cb foreign key (b_id) references B
);
It was not clear in the documentation that partition by reference is transitive ( A->B->C). I have discovered it by testing. we can create table C "partition by reference (fk_cb)" with the result that when we drop A partition the matching B and C partitions get dropped.

SQL Foreign key to two different tables

I have a problem like
Table A:
-- TableBCId
Table B:
-- Id
Table C:
-- Id
I am looking for a way to create a foreign key table A where an entry can be either in table B or table C
Example entries:
Table A:
-- TableBCId: 1
-- TableBCId: 2
Table B:
-- Id: 1
Table C:
-- Id: 2
I want to avoid if possible:
- Two columns in table A
- Default values
- Additional tables
- Creation of an base entity is not possible
Every idea welcome
The normal way to implement this requirement is with 2 columns, 2 foreign key constraints, and a check constraint to ensure exactly of of the columns populated (if this is a requirement):
create table a
( ...
, b_id references b
, c_id references c
, constraint <name> check ( (b_id is null and c_id is not null)
or (b_id is not null and c_id is null)
)
);
You could, if it helps your UI, create a view over that table that combines B_ID and C_ID into a single column.
But you have said you don't want 2 columns, why is that?
The reason why this is hard is because the data model is wrong. A foreign key references only one table. A table can have more than one foreign key but each is separate. Apart from anything else, how would you know whether bc_id referenced b.id or c.id?
One explanation for this scenario is that table A should really be two tables, BA referencing B and CA referencing C. Alternatively A should reference a super-type table, of which B and C are sub-types. Without knowing the actual business domain it's hard to be sure.
Anyway, the path of least change is two columns.
You can use a insert/update trigger on your Table_A.
Maybe something like this:
CREATE TRIGGER Table_a_trgr
BEFORE INSERT OR UPDATE
on Table_a
FOR EACH ROW
DECLARE
c_row NUMBER;
BEGIN
SELECT count(*)
INTO c_row
FROM (
SELECT ID FROM table_b WHERE id = :NEW.TableBCId
UNION ALL
SELECT ID FROM table_c WHERE İd = :NEW.TableBCId
)
;
IF c_row < 2 THEN
raise_application_error(-20000, 'Error, Foreign Key');
END IF;
END;
/
If you don't want to make a new column in table A.
Then you can make a parent table to B and C.
tableBC:
id
And then create a 1-1 relation with tables B and C to table BC.
tableB:
id,
parent -- 1-1 foreign key to tableBC
tableC:
id,
parent -- 1-1 foreign key to tableBC
Now, in table A
tableA:
id,
TableBCId -- foreign key to tableBC
We have solved a similar problem using this approach.

Why does SQLite not use an index for queries on my many-to-many relation table?

It's been a while since I've written code, and I never used SQLite before, but many-to-many relationships used to be so fundamental, there must be a way to make them fast...
This is a abstracted version of my database:
CREATE TABLE a (_id INTEGER PRIMARY KEY, a1 TEXT NOT NULL);
CREATE TABLE b (_id INTEGER PRIMARY KEY, fk INTEGER NOT NULL REFERENCES a(_id));
CREATE TABLE d (_id INTEGER PRIMARY KEY, d1 TEXT NOT NULL);
CREATE TABLE c (_id INTEGER PRIMARY KEY, fk INTEGER NOT NULL REFERENCES d(_id));
CREATE TABLE b2c (fk_b NOT NULL REFERENCES b(_id), fk_c NOT NULL REFERENCES c(_id), CONSTRAINT PK_b2c_desc PRIMARY KEY (fk_b, fk_c DESC), CONSTRAINT PK_b2c_asc UNIQUE (fk_b, fk_c ASC));
CREATE INDEX a_a1 on a(a1);
CREATE INDEX a_id_and_a1 on a(_id, a1);
CREATE INDEX b_fk on b(fk);
CREATE INDEX b_id_and_fk on b(_id, fk);
CREATE INDEX c_id_and_fk on c(_id, fk);
CREATE INDEX c_fk on c(fk);
CREATE INDEX d_id_and_d1 on d(_id, d1);
CREATE INDEX d_d1 on d(d1);
I have put in any index i could think of, just to make sure (and more than is reasonable, but not a problem, since the data is read only). And yet on this query
SELECT count(*)
FROM a, b, b2c, c, d
WHERE a.a1 = "A"
AND a._id = b.fk
AND b._id = b2c.fk_b
AND c._id = b2c.fk_c
AND d._id = c.fk
AND d.d1 ="D";
the relation table b2c does not use any indexes:
0|0|2|SCAN TABLE b2c
0|1|1|SEARCH TABLE b USING INTEGER PRIMARY KEY (rowid=?)
0|2|0|SEARCH TABLE a USING INTEGER PRIMARY KEY (rowid=?)
0|3|3|SEARCH TABLE c USING INTEGER PRIMARY KEY (rowid=?)
0|4|4|SEARCH TABLE d USING INTEGER PRIMARY KEY (rowid=?)
The query is about two orders of magnitude to slow to be usable. Is there any way to make SQLite use an index on b2c?
Thanks!
In a nested loop join, the outermost table does not use an index for the join (because the database just goes through all rows anyway).
To be able to use an index for a join, the index and the other column must have the same affinity, which usually means that both columns must have the same type.
Change the types of the b2c columns to INTEGER.
If the lookups on a1 or d1 are very selective, using a or d as the outermost table might make sense, and would then allow to use an index for the filter.
Try running ANALYZE.
If that does not help, you can force the join order with CROSS JOIN or INDEXED BY.

How to use Oracle "partitioning by reference" in tables with 2 foreign keys?

A little bit late in the project we have realized that we need to use Oracle (11G) partitioning both for performance and admin of the data.
We have an hierarchical entity model with lots of #OneToMany and #OneToOne relationships, and some entities are referenced from 2 or more other entities.
We want to use "partitioning by range" (month) on the "parent/root" entity and "partition by reference" on all child entities. After a year we will move the oldest month partition to an archive db. It's a 24-7 system so the data continuously grows.
From the Oracle documentation: "Reference partitioning allows the partitioning of two tables related to one another by referential constraints. The partitioning key is resolved through an existing parent-child relationship, enforced by enabled and active primary key and foreign key constraints."
Is it possible to use "partition by reference" on a table when there are 2 foreign keys and one of them can be null?
(From what I read you have to use "not null" on the foreign key for "partition by reference")
A small example to illustrate the problem:
A - parent entity
B - child entity to A
C - child entity to A or B
create table
A (
id number primary key,
adate date
)
partition by range (adate) (
partition p1 values less than (to_date('20130501','yyyymmdd')),
partition p2 values less than (to_date('20130601','yyyymmdd')),
partition pm values less than (maxvalue)
);
create table
B (
id number primary key,
text varchar2(5),
a_id number not null,
constraint fk_ba foreign key (a_id) references A
)
partition by reference(fk_ba);
create table
C (
id number primary key,
text varchar2(5),
a_id number not null, -- NOT POSSIBLE as a_id or b_id will be null..
b_id number not null, -- NOT POSSIBLE as a_id or b_id will be null..
constraint fk_ca foreign key (a_id) references A,
constraint fk_cb foreign key (b_id) references B
)
partition by reference(fk_ca)
partition by reference(fk_cb);
Thanks for any advice.
/Mats
You cannot partition by two foreign keys.
If A is parent of B and B is parent of C i would suggest partitioning C by fk_cb. weather you'l gain max pruning when joining A and C - thats an interesting question, why wont you run a test for us ?
question - why you have FK of A in table C. isn't A implied by the fk to B ?
(my guess, technically it's possible but oracle would have to access table B. i dont think he will do that so i think you wont get prunning).

Oracle Sql Update with values using foreign key?

Table 1:
Name, x1-X2, fk1, fk2.
Table 2:
K1(parent for table 1),X
How to update table 1 whose second column x1-x2 depands on fk1,fk2 from Table 2
Table1:
a,1.0,1,2
b,-3.0,2,3
Table 2
1,4.0
2,5.0
3,2.0
With this setup:
CREATE TABLE table2 (k NUMBER PRIMARY KEY, x NUMBER);
CREATE TABLE table1 (
NAME VARCHAR2(10) PRIMARY KEY,
diff NUMBER,
fk1 NUMBER REFERENCES table2,
fk2 NUMBER REFERENCES table2);
the following update will "refresh" the colum table1.diff with the values of table2:
SQL> UPDATE (SELECT child.diff old_diff, parent2.x - parent1.x new_diff
2 FROM table1 child
3 JOIN table2 parent1 ON (child.fk1 = parent1.k)
4 JOIN table2 parent2 ON (child.fk2 = parent2.k))
5 SET old_diff = new_diff
6 WHERE old_diff != new_diff
7 OR (old_diff IS NULL AND new_diff IS NOT NULL)
8 OR (old_diff IS NOT NULL AND new_diff IS NULL);
Only the rows that need updating will be refreshed (thanks to the where clause).
Not quite sure I understand the question, referential integrity constraints do not prevent you from updating a table. If you need to do a lot of work in a transaction you can look into Deferred Constraints. Is there any chance you could clarify what you mean?
Not sure exactly what the problem is, maybe you need to rephrase the question a little.
In general, the foreign key constraint makes sure that there exists a corresponding row in the referenced table.
When you update a row with a foreign key, and try to set a value that does not point to such a master row, you will get an error, either immediately or when you commit (depending on when the constraint is enforced, it can be deferred).
Other than that, the update is no different than any other update.
Since the data in table 1 is dependent on the data in table 2 you won't be able to "Update" table 1 directly.
You will have to perform the update on table 2 and then recalculate table 1.
You could do it inside a transaction or perhaps a trigger on table 2.
Another option may be to have table one only hold the foreign keys and the name and then create a view that calculates the X1-X2 value.
EDIT
After looking at the sample data, I don't think you can unambiguously have table 2 be updates as a result of an update on table 1.
For example if you update the second column of table 1 to be 43, how would you know what values to set the specific rows of table 2 (it could be 40 and 3, 20 and 23, etc.)

Resources