CREATE TABLE1 (
ID INT NOT NULL PRIMARY KEY
FOREIGN KEY ID REFERENCES TABLE2 (ID)
)
CREATE TABLE2 (
ID INT NOT NULL,
OTHER INT NOT NULL
PRIMARY KEY (ID, OTHER)
FOREIGN KEY ID REFERENCES TABLE1 (ID)
)
Table 1 and Table 2 are big tables containing separate sets of information.
They have a one-to-one relationship requiring full participation from both sides. Where should I put the foreign key statement? In Table1, Table2, or both? And why?
There is nothing particularly wrong with doing it, however you should put the foreign key in both tables. That means when you insert new values you'll have to start a transaction, do both inserts, then commit the transaction.
I would strongly recommend merging the tables into one table. It will make everything easier.
Id to id relationship is considered . This is a great trick in implementation .
Related
I have a situation while database designing, A simple issue but needed a working suggestions
My database tables:
TableAees.
TableBees.
Aees can mapped/contain one or more records of table Bees or also can be without any Bees
Aees can also mapped with one or more records of table Aees itself
Here normal primary key and foreign key relationship/hierarchy won't solve the purpose and also worried that parent/child hierarchy may end up in forming a loop between tables and can give a duplicates records on various joins.
Need a better table mapping for above mentioned tables(a,b) which will satisfy 1 and 2 points.
So to avoid such a situation, how the table relationship/hierarchy will be a better approach?
Database used: SQL Server
Thanks for sharing your knowledge.
You seem to describe a many-to-many relationship. If so, you would create a thrid table to store that relationship, like so:
create table a (
a_id int primary key,
...
);
create table b (
b_id int primary key,
...
);
create table ab (
a_id int references a(a_id),
b_id int references b(b_id),
primary key (a_id, b_id)
)
Each a/b tuple is stored on a separate row in bridge table ab.
I have tables:
MUSICIANS (musician_id, ...)
PROGRAMMERS (programmer_id, ...)
COPS (cop_id, ...)
Then I'm going to have a specific table
RICH_PEOPLE (rich_person_id, ...)
where rich_person_id is either musician_id, programmer_id or cop_id. (Assume that all the musician_ids, programmer_ids, cop_ids are different.)
Is it possible to directly create a Foreign Key on the field rich_person_id?
P.S. I would like the database to
ensure that there is a record of either MUSICIANS, PROGRAMMERS or COPS with the same id as the new RICH_PEOPLE record's rich_person_id before inserting it into RICH_PEOPLE
deleting from either MUSICIANS, PROGRAMMERS or COPS would fail (or require cascade deletion) if there a RICH_PEOPLE record with the same id
P.P.S. I wouldn't like
creating an extra table like POSSIBLY_RICH_PEOPLE with the only field possibly_rich_person_id
creating triggers
You can create three nullable foreign keys, one to each foreign table. Then use a CHECK constraint to ensure only one value is not null at any given time.
For example:
create table rich_people (
rich_person_id int primary key not null,
musician_id int references musicians (musician_id),
programmer_id int references programmers (programmer_id),
cop_id int references cops (cop_id),
check (musician_id is not null and programmer_id is null and cop_id is null
or musician_id is null and programmer_id is not null and cop_id is null
or musician_id is null and programmer_id is null and cop_id is not null)
);
This way, referential integrity will be ensured at all times. Deletions will require cascade deletion or other strategy to keep data integrity.
You do this in a somewhat different way:
Create a table people with a person_id.
Use this key as the primary key (and foreign key) for each of your occupation tables.
Use this key as the primary key (and foreign key) for your rich_people table.
Postgres supports a concept called "inheritance", which facilitates this type construct. Your occupation tables can "inherit" columns from people.
Consider a situation where I define an object, a group of objects, then a table that links them together:
CREATE TABLE obj (
id INTEGER PRIMARY KEY,
name text
) ;
CREATE TABLE group (
id INTEGER PRIMARY KEY ;
grpname TEXT
) ;
CREATE TABLE relation (
objid INTEGER,
grpid INTEGER,
PRIMARY KEY (objid, grpid)
) ;
I am looking for cascade delete when applicable so I add the foreign key
ALTER TABLE relation
ADD FOREIGN KEY (objid)
REFERENCES obj(id)
ON DELETE CASCADE ;
ALTER TABLE relation
ADD FOREIGN KEY (grpid)
REFERENCES group(id)
ON DELETE CASCADE ;
So far is all OK. Now suppose I want to add support for group of groups. I am thinking to change the relation table like this:
CREATE TABLE relation_ver1 (
parent INTEGER,
child INTEGER,
PRIMARY KEY (parent, child)
) ;
ALTER TABLE relation_ver1
ADD FOREIGN KEY (parent)
REFERENCES group(id)
ON DELETE CASCADE ;
Here I get to the question: I would like to apply cascade delete to child too, but I do not know here if child refers to a group or object.
Can I add a foreign key to table obj or group?
The only solution I have found do fare is add child_obj and child_grp fields, add the relative foreign keys and then, when inserting e.g an object use a 'special' (sort of null) group, and do the reverse when inserting subgroup.
Consider the relation:
relation_ver1(parent, child_obj, child_group)
I claim that this relation has the following disadvantages:
You have to deal with the NULL special case.
Approx. 1/3 of values are NULL. NULL values are bad.
Fortunately, there is an easy way to fix this. Since there is a multi-value dependency in your data, you can decompose your table into 2 smaller tables that are 4NF compliant. For example:
relation_ver_obj(parent, child_obj) and
relation_ver_grp(parent, child_group).
The primary reason why we have foreign keys is not so as to be able to do things like cascaded deletes. The primary reason for the existence of foreign keys is referential integrity.
This means that grpid is declared as REFERENCES group(id) in order to ensure that grpid will never be allowed to take any value which is not found in group(id). So, it is an issue of validity. A cascaded DELETE also boils down to validity: if a key is deleted, then any and all foreign keys referring to that key would be left invalid, so clearly, something must be done about them. Cascaded deletion is one possible solution. Setting the foreign key to NULL, thus voiding the relationship, is another possible solution.
Your notion of having a child id refer to either a group or an object violates any notion of referential integrity. Relational Database theory has no use and no provision for polymorphism. A key must refer to one and only one kind of entity. If not, then you start running into problems like the one you have just discovered, but even worse, you cannot have any referential integrity guarantees in your database. That's not a nice situation to be in.
The way to handle the need of relationships to different kinds of entities is with the use of a set of foreign keys, one for each possible related entity, out of which only one may be non-NULL. So, here is how it would look like:
CREATE TABLE tree_relation (
parent_id INTEGER,
child_object_id INTEGER,
child_group_id INTEGER,
PRIMARY KEY (parent_id, child_object_id, child_group_id) );
ALTER TABLE tree_relation
ADD FOREIGN KEY (parent_id) REFERENCES group(id) ON DELETE CASCADE;
ALTER TABLE tree_relation
ADD FOREIGN KEY (child_object_id) REFERENCES object(id) ON DELETE CASCADE;
ALTER TABLE tree_relation
ADD FOREIGN KEY (child_group_id) REFERENCES group(id) ON DELETE CASCADE;
All you need to do is ensure that only one of child_object_id, child_group_id is non-NULL.
Need advice of the best approach how to design DB for the following scenario:
Following below DB structure exmaple (it's not real just explain problem)
File
(
Id INT PRIMARY KEY...,
Name VARCHAR(),
TypeId SMALLINT,
...
/*other common fields*/
)
FileContent
(
Id INT PRIMARY KEY...,
FileId FOREIGN KEY REFERENCES File(Id) NOT NULL ON DELETE CASCADE UNIQUE,
Content VARBINARY(MAX) NOT NULL,
)
Book
(
Id INT PRIMARY KEY...,
Name VARCHAR(255),
Author VARCHAR(255)
...
CoverImageId FK REFERENCES File(Id),
)
BookPageType
(
Id TINYINT PRIMARY KEY...,
Name VARCHAR(50),
)
BookPage
(
Id INT PRIMARY KEY...,
TypyId TINYINT FOREIGN KEY REFERENCES BookPageType(Id),
BookId INT FOREIGN KEY REFERENCES Book(Id) ON DELETE CASCADE,
Name VARCHAR(100),
CreatedDate DATETIME2,
...
/*other common fields*/
)
BookPage1
(
Id PRIMARAY KEY REFERENCES BookPage(Id) NOT NULL ON DELETE CASCADE,
FileId PRIMARAY KEY REFERENCES File(Id)
...
/* other specific fileds */
)
...
BookPageN
(
Id PRIMARAY KEY REFERENCES BookPage(Id) NOT NULL ON DELETE CASCADE,
ImageId PRIMARAY KEY REFERENCES File(Id),
...
/* other specific fileds */
)
Now question is I want to delete Book with all pages and data (and it works good with delete cascade), but how to make cascade delete the associated files also (1 to 1 relentionship).
Here I see following approaches:
Add file to every table when I use it, but I don't want to copy file
schema for every table
Add foreign keys to the File table (instead of page for example), but since I use file for e.g. in 10 tables I will have 10 foreign keys in file table. This also not good
Use triggers, what I don't wnat to do
Thanks in Advance
If such necessary is appeared maybe it seems you need refactor your base.
You said this example is not real and I'll not ask about N tables for pages though it's strange. If not all files have 1 to 1 relationship and so you need remove only a file that other book does not refer to, it's sounds like a job for a trigger.
So what you have defined is a many-to-many relationship between BookPage and File. this is a result of the one-to-many relationship between BookPage and BookPageN and then the one-to-many relationship between File and BookPageN. To get the relationships you say you want in the text, you need to turn the relationship around to point from BookPageN to File. Maybe instead of having so many BookPageN tables you could find a way to consolidate them into a single table. Maybe just use the BookPage table. Just allow nulls for the fields that are optional.
This isn't a big deal, but my OCD is acting up with the following problem in the database I'm creating. I'm not used to working with databases, but the data has to be stored somewhere...
Problem
I have two tables A and B.
One of the datafields is common to both tables - segments. There's a finite number of segments, and I want to write queries that connect values from A to B through their segment values, very much asif the following table structure was used:
However, as you can see the table Segments is empty. There's nothing more I want to put into that table, rather than the ID to give other table as foreign keys. I want my tables to be as simple as possible, and therefore adding another one just seems wrong.
Note also that one of these tables (A, say) is actually master, in the sense that you should be able to put any value for segment into A, but B one should first check with A before inserting.
EDIT
I tried one of the answers below:
create table A(
id int primary key identity,
segment int not null
)
create table B(
id integer primary key identity,
segment int not null
)
--Andomar's suggestion
alter table B add constraint FK_B_SegmentID
foreign key (segment) references A(segment)
This produced the following error.
Maybe I was somehow unclear that segments is not-unique in A or B and can appear many times in both tables.
Msg 1776, Level 16, State 0, Line 11 There are no primary or candidate
keys in the referenced table 'A' that match the referencing column
list in the foreign key 'FK_B_SegmentID'. Msg 1750, Level 16, State 0,
Line 11 Could not create constraint. See previous errors.
You can create a foreign key relationship directly from B.SegmentID to A.SegmentID. There's no need for the extra table.
Update: If the SegmentIDs aren't unique in TableA, then you do need the extra table to store the segment IDs, and create foreign key relationships from both tables to this table. This however is not enough to enforce that all segment IDs in TableB also occur in TableA. You could instead use triggers.
You can ensure the segment exists in A with a foreign key:
alter table B add constraint FK_B_SegmentID
foreign key (SegmentID) references A(SegmentID)
To avoid rows in B without a segment at all, make B.SegmentID not nullable:
alter table B alter column SegmentID int not null
There is no need to create a Segments table unless you want to associate extra data with a SegmentID.
As Andomar and Mark Byers wrote, you don't have to create an extra table.
You can also CASCADE UPDATEs or DELETEs on the master. Be very carefull with ON DELETE CASCADE though!
For queries use a JOIN:
SELECT *
FROM A
JOIN B ON a.SegmentID = b.SegmentID
Edit:
You have to add a UNIQUE constraint on segment_id in the "master" table to avoid duplicates there, or else the foreign key is not possible. Like this:
ALTER TABLE A ADD CONSTRAINT UNQ_A_SegmentID UNIQUE (SegmentID);
If I've understood correctly, a given segment cannot be inserted into table B unless it has also been inserted into table A. In which case, table A should reference table Segments and table B should reference table A; it would be implicit that table B ultimately references table Segments (indirectly via table A) so an explicit reference is not required. This could be done using foreign keys (e.g. no triggers required).
Because table A has its own key I assume a given segment_ID can appear in table A more than once, therefore for B to be able to reference the segment_ID value in A then a superkey would need to be defined on the compound of A_ID and segment_ID. Here's a quick sketch:
CREATE TABLE Segments
(
segment_ID INTEGER NOT NULL UNIQUE
);
CREATE TABLE A
(
A_ID INTEGER NOT NULL UNIQUE,
segment_ID INTEGER NOT NULL
REFERENCES Segments (segment_ID),
A_data INTEGER NOT NULL,
UNIQUE (segment_ID, A_ID) -- superkey
);
CREATE TABLE B
(
B_ID INTEGER NOT NULL UNIQUE,
A_ID INTEGER NOT NULL,
segment_ID INTEGER NOT NULL,
FOREIGN KEY (segment_ID, A_ID)
REFERENCES A (segment_ID, A_ID),
B_data INTEGER NOT NULL
);