How to prevent cyclic relation between keys on postgresql? - database

There's a table called consists_of which relates products categories hierarchy.
CREATE TABLE consists_of(
super_name VARCHAR(80),
sub_name VARCHAR(80),
UNIQUE(super_name, sub_name),
PRIMARY KEY(sub_name),
FOREIGN KEY(sub_name) REFERENCES category(name),
FOREIGN KEY(super_name) REFERENCES category(name),
CHECK (sub_name <> super_name)
);
How can I prevent a cyclic relationship, for example:
INSERT INTO consists_of values ('Cheese', 'Cheddar')
INSERT INTO consists_of values ('Cheddar', 'Cheese')

To prevent the second INSERT statement, this index would be sufficient:
CREATE UNIQUE INDEX ON consists_of (
least(super_name, sub_name),
greatest(super_name, sub_name)
);
But that won't prevent cycles of three or more entries.
One thing I could imagine is to have a level with each category. Then you can enforce that the level of the sub_name must be lower than the level of super_name.

Related

How to lock multiple table rows in postgresql?

I have two tables, i will leave the create table statements for both of them below.
I would like to know if it is possible to lock the corresponding rows in currency_details when someone is modifying a row on currency and vice versa.
CREATE TABLE IF NOT EXISTS currency (
id VARCHAR(8),
name VARCHAR(20),
PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS currency_details (
id VARCHAR(8) REFERENCES currency ON DELETE CASCADE,
info VARCHAR(80),
PRIMARY KEY (id)
);
The only locks that are taken automatically is a FOR KEY SHARE row lock on currency if you insert rows into currency_details.
Other than that, you could write a trigger that performs an appropriate SELECT ... FOR UPDATE on the other table.
But that is an unusual requirement; perhaps it is not really necessary to lock those rows.

T-SQL Trigger or Constraint for Gaps in Intervals

I have a table of identifiers, IntervalFrom and IntervalTo:
Identifier
IntervalFrom
IntervalTo
1
0
2
1
2
4
2
0
2
2
2
4
I already have a trigger to NOT allow the intervals to overlap.
I am looking for a trigger or constraint that will not allow data gaps.
I have search and the information I found relates to gaps in queries and data rather than not allowing them in the first place.
I am unable to find anything in relation to this as a trigger or constraint.
Is this possible using T-SQL?
Thanks in advance.
You can construct a table that automatically is immune from overlaps and gaps:
create table T (
ID int not null,
IntervalFrom int null,
IntervalTo int null,
constraint UQ_T_Previous_XRef UNIQUE (ID, IntervalTo),
constraint UQ_T_Next_XRef UNIQUE (ID, IntervalFrom),
constraint FK_T_Previous FOREIGN KEY (ID, IntervalFrom) references T (ID, IntervalTo),
constraint FK_T_Next FOREIGN KEY (ID, IntervalTo) references T (ID, IntervalFrom)
)
go
create unique index UQ_T_Start on T (ID) where IntervalFrom is null
go
create unique index UQ_T_End on T(ID) where IntervalTo is null
go
Note, this does require a slightly different convention for you first and last intervals - they need to use null rather than 0 or the (somewhat arbitrary) 4.
Note also that modifying data in such a table can be a challenge - if you're inserting a new interval, you also need to update other intervals to accommodate the new one. MERGE is your friend here.
Given the above, we can insert your (modified) sample data:
insert into T (ID, IntervalFrom, IntervalTo) values
(1,null,2),
(1,2,null),
(2,null,2),
(2,2,null)
go
But we cannot insert an overlapping value (this errors):
insert into T(ID, IntervalFrom, IntervalTo) values (1,1,3)
You should also see that the foreign keys prevent gaps from existing in a sequence

Deal with many to many relationships in the same table

I have a table of business names and info about them, including an address column. A business name can have duplicate names and an address can have duplicates, but both the Business name and address cannot be duplicates. So there's a many to many relationship of sorts, but that is not between two tables, it's between two columns on the same table.
One or both of these fields also need to be a foreign key of another table.
Do I create a bridge table? Composite keys? If composite keys, how would I make them foreign keys in another table?
You might want to create 3 tables. One that stores the business names, another for the adresses, and then a junction table called, for example, business_adresses.
create table businesses (
id int primary key,
name varchar(50)
);
create table adresses (
id int primary key,
street varchar(200),
city varchar(200),
country varchar(200)
);
create table business_adresses(
business_id int,
adress_id int,
primary key (business_id, adress_id),
foreign key (business_id) references businesses(id),
foreign key (adress_id) references adresses(id)
);
With this set up, each entity has its own table, and information is not duplicated, unlike when using a single table. Meanwhile, in the junction table, you can efficiently enfore the unicity of business/entity tuples through the primary key (could also be a unique key), and maintain data integrity with foreign keys.

One-One relationship constraint

i actually want to implement a compulsory one-one relationship between two tables from my ERD in Oracle. The two tables are Governor and State. A governor can govern only one state and a state must have one and only one governor. I want to implement that in Oracle. I have written the queries as follows
create table gov
(gid number(3) ,name varchar2(100),
constraint gov_pk primary key (gid)
);
create table state
(
sid number(3) ,
name varchar2(100),
gid number(3),
constraint state_pk primary key (sid),
constraint gov_state_fk foreign key (gid) references gov(gid),
constraint state_uk unique(gid,name)
);
But that does not seem to work. I could not find any alternative way. Please help me with this. I will be thankful to you. And please let me know why is is failing to establish one-one relationship.
You're pretty close to successfully implementing your requirements.
" a state must have one and only one governer"
So make GID mandatory on STATE table.
"A governer can govern only one state "
So enforce a unique key on just GID.
create table state
(
sid number(3) ,
name varchar2(100),
gid number(3) not null,
constraint state_pk primary key (sid),
constraint gov_state_fk foreign key (gid) references gov(gid),
constraint state_uk unique(gid)
);
"I can successfully add data to gov table with out adding any row in state table."
Enforcing a Parent must have a Child relationship is pretty hard.
SQL standards have the concept of Assertions, which could enforce that kind of business rule, but Oracle (nor any other DBMS vendor) have not implemented them.
a foreign key on GOV referencing STATE is right out, because circular dependencies are deadly.
that leaves us with a trigger on GOV.
Here is such a trigger:
create or replace trigger enforce_gov_state
before insert or update on gov
for each row
is
l_sid state.sid%type;
begin
select s.sid into l_sid
from state s
where s.gid = :new.gid;
exception
when no_data_found then
raise_application_error(-20000, 'Governor must have a state');
end;
/
So that's okay then. Just one little wrinkle: how do we insert rows into either table???? We can't insert into GOV until the state exists; we can't insert into STATE until the governor exists.
There is a workaround: defer the foreign key on STATE so it's not enforced until the whole transaction is committed. This permits the creation of STATE record followed by GOV record. Of course, we need to know the value of STATE.GID before we create the GOV record.
Also, there are similar snags attached to changing the GOV - STATE relationship. Except that it can be solved by updating all the GOV attributes (except GID) to fit a new Governor. Which is kind of sketchy but there you go.
Why does Oracle make this so hard? Often a one-to-one relationship between tables which is mandatory on both sides points to a flawed data model.
Sometimes 1:1 points to a single table. That is unsatisfactory when
we have two distinct entities such as here.
More likely the 1:1
relationship is wrong, and it's actually 1:N or even M:N. Consider
that a State can have many Governors, one current, many previous and
optionally one elect. Likewise a politician can theoretically be
Governor of more than one state over the course of a career.
So a more truthful implementation would have STATE_GOV as an intersection table between STATE and GOV. It is much simpler to maintain such a table, which is a good sign.
Remove FK from state table. Having it and making it unique means you can't enter a state without already knowing the governor. Create an intersection table between State and Gov with a unique constraint on each FK:
create table StateGov(
StateID number( 3 ) not null references State( sid ),
GovID number( 3 ) not null references Gov( gid ),
constraint UQ_StateGov_State unique StateID,
constraint UQ_StateGov_Gov unique GovID
);
No state can appear more than once, no governor can appear more than once. No circular references, no assertions, no problem with inserting a state record before you know the governor.
Add a unique constraint to STATE:
create table state
(
sid number(3) ,
name varchar2(100),
gid number(3),
constraint state_pk primary key (sid),
constraint gov_state_fk foreign key (gid) references gov(gid),
constraint state_uk unique(gid,name)
constraint gov_state_uk unique (gid)
);

Relationships between tables

I have a table called objectives, each objective has zero to many cause-effect relationships with other objectives, these relationships I have to be stored in the database, let me know if there's a way to relate this table records.
There is not a way to relate the records without creating an additional table (you would need N-1 additional columns on your current table to model the N possible effects of a cause).
Creating an additional table like the one below should serve your purpose.
CREATE TABLE cause_effect (
cause integer NOT NULL,
effect integer NOT NULL,
CONSTRAINT cause_effect_pkey PRIMARY KEY (cause, effect),
CONSTRAINT cause_effect_cause_fkey FOREIGN KEY (cause)
REFERENCES yourtable (id),
CONSTRAINT cause_effect_effect_fkey FOREIGN KEY (effect)
REFERENCES yourtable (id)
)
Apply FKey behaviour as applies.

Resources