Have a foreign key reference multiple tables? - sql

I have 3 tables:
Shirts(model, color)
Gloves(model, color)
Socks(model, color)
Where model is primary key in all 3 tables
Now I need to make another table:
Products (model, price)
So, in products I need to get all models from my first 3 tables, in one single column. How can I do that?

In my opinion, you've designed it wrong. Suggestion (as a comment under the question, saying that products should be referenced by 3 another tables) is - again, in my opinion - wrong.
You shouldn't create separate tables for shirts, gloves or socks. What will you do when you start selling hats or trousers or shoes? Will you create new tables for all of those? Of course not - those are just clothes (products) types.
So - create one table that contains all types; when new type appears, simply add a new row into that table and reference it from the products table.
Something like this:
SQL> create table product_type
2 (id_type number primary key,
3 name varchar2(30)
4 );
Table created.
SQL> create table products
2 (id_product number primary key,
3 id_type number references product_type,
4 color varchar2(20),
5 price number
6 );
Table created.
SQL> insert into product_type
2 select 1, 'shirt' from dual union all
3 select 2, 'glove' from dual union all
4 select 3, 'socks' from dual;
3 rows created.
SQL> insert into products
2 -- shirt
3 select 100, 1, 'red', 100 from dual union all
4 -- gloves
5 select 101, 2, 'blue', 150 from dual;
2 rows created.
SQL>
Here come the shoes:
SQL> insert into product_type
2 select 4, 'shoes' from dual;
1 row created.
SQL> insert into products
2 select 113, 4, 'brown', 400 from dual;
1 row created.
SQL>
Conclusion: read about normalization.
If someone says that "colors should be in a separate table", well - perhaps, that wouldn't be a bad idea either. Furthermore, does the products table have to be expanded by a date column (which would show what price was valid at certain period)? Not a bad idea either. There are numerous options you can include into the model. I just tried to point you into the right direction.

A foreign key can be created from the Products table to each of the other tables. However, creating a foreign key from each of the other tables (Shirts, Gloves, Socks) is not possible (unless each Product exists in all three tables). A foreign key from Shirts, Gloves, and Socks would basically say: before an insert into the Products table ensure that there is a record in each of the three other tables with the same key.
As suggested by the other posters, it is probably better to consider a slightly different design (e.g. add a type column to the products table).
Also, by convention table names should be singular (Product, Shirt, Glove, Sock).

If you mean in an environment like phpMyAdmin, MySQLWorkbench etc. you can just manually go to each product table 'Shirt','Gloves','Socks' and add a reference/foreign key of the field 'Model' to the the field 'Model' in the table 'Product'. The way to do this depends entirely on the programm you are using. Note that ofcourse you should create the new Table either manually or by SQL code before you do this.
There is no problem for multiple tables to point to same other table via foreign keys.
If you want to write SQL code to this you can use something like this, which is the other way around (use three foreign keys in the table 'Product', one for each other table). Note that the tables Socks,Shirts and Gloves must have already been created.
DROP TABLE IF EXISTS `Product` ;
CREATE TABLE `Product` (
`Color` varchar(8),
`Model` varchar(14),
PRIMARY KEY(`Model`),
FOREIGN KEY('Model') REFERENCES `Socks`(`Model`) ON DELETE CASCADE,
FOREIGN KEY('Model') REFERENCES `Gloves`(`Model`) ON DELETE CASCADE,
FOREIGN KEY('Model') REFERENCES `Shirts`(`Model`) ON DELETE CASCADE
) ;
The way you want is something like this (done three times one for each table). Note that as i said before the table Product must have been already created.
DROP TABLE IF EXISTS `Socks` ;
CREATE TABLE `Socks` (
`Color` varchar(8),
`Model` varchar(14),
PRIMARY KEY(`Model`),
FOREIGN KEY('Model') REFERENCES `Product`(`Model`) ON DELETE CASCADE
);

Related

Violation of primarary key

I am trying to create a many-to-many relationship between two tables Asset and Inventory, I created an intermediate table called Asset_Inventory
One or more assets can be in an inventory, and an inventory can be in one or more assets,but it does not allow me to insert data
Mistake
The data in row 2 was not committed. Error source Message: Violation of PRIMARY KEY CONSTRAINT
PK_INVENTORY_ASSET. Cannot insert duplicate key in object dbo.Inventory_Asset. The duplicate key value (1,1).
-- TABLE INVENTORY
GO
CREATE TABLE Inventory
(
[inventory_id] INT NOT NULL IDENTITY(1,1),
[company_id] INT,
[name] VARCHAR(50),
[observations] VARCHAR(500),
[date_created] DATETIME DEFAULT(GETDATE()),
CONSTRAINT PK_INVENTORY PRIMARY KEY (inventory_id),
CONSTRAINT FK_INVENTORY_COMPANY FOREIGN KEY(company_id) REFERENCES Company(company_id)
)
-- TABLE ASSET
GO
CREATE TABLE Asset
(
[asset_id] INT NOT NULL IDENTITY(1,1),
[company_id] INT,
[code] INT,
[model_name] VARCHAR(50),
[model_number] VARCHAR(50),
[serial_number] VARCHAR(30),
[price] DECIMAL(10,2),
CONSTRAINT PK_ASSET_ID PRIMARY KEY(asset_id),
CONSTRAINT FK_ASSET_COMPANY FOREIGN KEY(company_id) REFERENCES Company(company_id),
CONSTRAINT UQ_ASSET_CODE UNIQUE(code)
)
--TABLE INVENTORY_ASSET
CREATE TABLE Inventory_Asset
(
asset_id INT,
inventory_id INT,
CONSTRAINT PK_INVENTORY_ASSET PRIMARY KEY (asset_id,inventory_id),
CONSTRAINT FK_ASSET_ID FOREIGN KEY (asset_id) REFERENCES Asset(asset_id),
CONSTRAINT FK_IVENTORY_ID FOREIGN KEY (inventory_id) REFERENCES Inventory(inventory_id)
)
UPDATED
it's hard for me to ask this question
N companies have many assets; you need to have control over them. Have a list of assets by company, to which employee it was assigned, or in which department of the company that asset is located. a total of assets by company etc..
What is the purpose of this?
I need to physically label (bar code) each asset with a unique code
Cell phones, monitors, keyboards, servers, PCs, laptops, chairs, furniture, etc...
Once all the assets have been tagged in the
company 1
company 2
company 3
I have to scan each code printed on the asset and make a monthly cut
I have to create a report with this information
In the company 1 an inventory was made in the month of January
Cut of the month of January:
20 monitors (10 dell , 10hp)
20 chairs
10 computers
the next month this cut is made again to see if there is the same quantity, if the 2 inventories of the month of January and February are not the same, some asset was stolen and to be able to identify this
an inventory can have one or more assets, and an asset can be in one or more inventories.
I think I can do this with the Inventory_Asset table
asset_id
iventory_id
1
1
1
1
2
1
3
1
1
1
1
2
2
2
1
2
6
2
1
3
4
3
3
3
1
3
Am I wrong with my solution?
--TABLE INVENTORY_ASSET
CREATE TABLE Inventory_Asset
(
[inventory_id] INT,
[asset_id] INT,
CONSTRAINT FK_ASSET_ID FOREIGN KEY (asset_id) REFERENCES Asset(asset_id),
CONSTRAINT FK_IVENTORY_ID FOREIGN KEY (inventory_id) REFERENCES Inventory(inventory_id)
)
CONSTRAINT PK_INVENTORY_ASSET PRIMARY KEY (asset_id,inventory_id),
PRIMARY KEYS must be unique. That is why you can't add this -- since it conflicts with your primary key. If you don't want that to be unique but you do expect to use it as an index you can make an index that does not enforce uniqueness and make something else your primary key.
The answers and comments here are all correct; but perhaps we need to review what a many-to-many relation can be used for.
Option 1: we just need a link between two entities
This is what you have done. Basically it will serve if you want to know in which Inventories a given Asset is, which Assets a given Inventory holds, etc. In this scenario when you have established the link once (Asset 1 is in Inventory 1) there's no reason to re-establish it with further records.
Option 2: we want to store some information about the relation
This is what I suspect you really wanted. Suppose for instance you want to store how many Assets of type 1 are in Inventory 1. In this case what you want is to add a "Quantity" column to your Inventory_asset table. Once more, if there are 3 Assets of type 1 in Inventory 1 you won't duplicate the records: you will just create one record and put 3 in the new column.
Option 3: we really need multiple records
Suppose you want to store not only how many Assets of a given type are in a given Inventory, but also on which date a few of them were put there. So you may have 2 type 1 Assets put in Inventory 1 on May, 1st, and other 3 of them on May, 15th. In this case the date must become part of the PK, so that the system (and you too!) can distinguish between the different records. You may also decide to create an auto-increment column and use it as a PK instead; but note that while it may be simpler to handle, it would allow for unvoluntarily creating duplicate records, so I wouldn't recommend it.

How to reference foreign key from more than one column (Inconsistent values)

I Have table three tables:
The first one is emps:
create table emps (id number primary key , name nvarchar2(20));
The second one is cars:
create table cars (id number primary key , car_name varchar2(20));
The third one is accounts:
create table accounts (acc_id number primary key, woner_table nvarchar2(20) ,
woner_id number references emps(id) references cars(id));
Now I Have these values for selected tables:
Emps:
ID Name
-------------------
1 Ali
2 Ahmed
Cars:
ID Name
------------------------
107 Camery 2016
108 Ford 2012
I Want to
Insert values in accounts table so its data should be like this:
Accounts:
Acc_no Woner_Table Woner_ID
------------------------------------------
11013 EMPS 1
12010 CARS 107
I tried to perform this SQL statement:
Insert into accounts (acc_id , woner_table , woner_id) values (11013,'EMPS',1);
BUT I get this error:
ERROR at line 1:
ORA-02291: integrity constraint (HR.SYS_C0016548) violated - parent key not found.
This error occurs because the value of woner_id column doesn't exist in cars table.
My work require link tables in this way.
How Can I Solve This Problem Please ?!..
Mean: How can I reference tables in previous way and Insert values without this problem ?..
One-of relationships are tricky in SQL. With your data structure here is one possibility:
create table accounts (
acc_id number primary key,
emp_id number references emps(id),
car_id number references car(id),
id as (coalesce(emp_id, car_id)),
woner_table as (case when emp_id is not null then 'Emps'
when car_id is not null then 'Cars'
end),
constraint chk_accounts_car_emp check (emp_id is null or car_id is null)
);
You can fetch the id in a select. However, for the insert, you need to be explicit:
Insert into accounts (acc_id , emp_id)
values (11013, 1);
Note: Earlier versions of Oracle do not support virtual columns, but you can do almost the same thing using a view.
Your approach should be changed such that your Account table contains two foreign key fields - one for each foreign table. Like this:
create table accounts (acc_id number primary key,
empsId number references emps(id),
carsId number references cars(id));
The easiest, most straightforward method to do this is as STLDeveloper says, add additional FK columns, one for each table. This also bring along with it the benefit of the database being able to enforce Referential Integrity.
BUT, if you choose not to do, then the next option is to use one FK column for the the FK values and a second column to indicate what table the value refers to. This keeps the number of columns small = 2 max, regardless of number of tables with FKs. But, this significantly increases the programming burden for the application logic and/or PL/SQL, SQL. And, of course, you completely lose Database enforcement of RI.

SQL DB table Relationship

I am trying to create a small DB based on flat files that I import from a non Database System. The Import is working, the DB is good but I added a new table that contains data from another system. I am trying to create a relationship between the tables but because one table has duplicate rows (flat file is the source) I am not able to set that relationship.
Example: Table 1 lists all procedures done for a patient by a physician.. the patient can have many of the same procedures on the same day by the same physician (hence the duplicate rows) ... Table 2 has a list of Physicians and their ID #s ... I want to set up a relationship between the two tables based on the physician's name but I am getting errors because of the non unique data.
Anyone has a tip?
Thanks
the patient can have many of the same procedures on the same day by the same physician (hence the duplicate rows)
Normally, you should be able to set a foreign key relationship from Table1 to Table2 even in the presence of duplicate rows. This kind of error usually means you're trying to set the foreign key in the wrong table.
-- Your "Table2"
create table physicians (
physician_id integer primary key,
physician_name varchar(35) not null -- names are not unique
);
insert into physicians values
(1, 'Doctor Who'), (2, 'Dr. Watson');
create table patients (
patient_id integer primary key,
patient_name varchar(35) not null -- names are not unique
);
insert into patients values
(100, 'Melville, Herman'), (101, 'Poe, Edgar Allen');
-- Your "Table1"
-- Allows multiple physicians per date.
create table patient_procedures(
patient_id integer not null references patients (patient_id),
physician_id integer not null references physicians(physician_id),
procedure_date date not null default current_date,
procedure_name varchar(15) not null,
primary key (patient_id, physician_id, procedure_date, procedure_name)
);
insert into patient_procedures values
(100, 1, '2012-01-02', 'CBC'),
(100, 1, '2012-01-02', 'Thyroid panel');
I'm not sure from your description where the duplicate-data problem is. You have:
Table 1: procedures. Could be lots of rows for same physician
Table 2: physicians. Should be 1 row per physician (but there may be duplicates)
The relationship that would make sense would be 1[table 2 physician row] -> many[table 1 procedures rows]. i.e. table 2 would be the primary key table in the relationship: each table 2 row relating to between 0 and "many" table 1 rows. If you try to create this kind of relationship, then multiple duplicate table 1 rows are not a problem.
If, however, you have multiple rows per physician in table 2, then you won't be able to create this kind of relationship, because table 2 rows are not unique and thus can't act as the primary key element in the relationship. The problem then is one of data-cleansing: figuring out which rows in table 2 are duplicates, updating the table 1 rows to point to just ONE physician out of the duplicates, and then deleting the duplicate rows from table 2.
You mention physician ID#s and physician names. Physician name would be a bad choice for a unique key; if a user tries to add a new physician called "John Smith" when there already is another physician of that name, either
You've set up a unique index on PhysicianName, their change gets rejected, and you have an irate user; or
You haven't, and all the existing physician's (let's call him John A. Smith) procedures will be associated with the other physician (let's call him John B. Smith) and vice versa.
The relationship should be set up using the physician ID. If Table 1 (Procedures) includes a physician ID column, you're in luck. If it only includes physician Name, then you may have a data-cleansing problem, if there are already duplicate physician Names in Table 2.

constrainging database with alternate foreign keys

I have a database table that I am using as an interface between systems. I write to it, they read from it. I have a foreign key in the table that references my user table.
Now the interface is getting more complex. I now have another use for the interface table that doesn't reference the user table. I can of course relax the foreign key constraint to user table.
What I want to do is have a constraint state either this foreign key is satisfied OR this other key (in different column) is satisfied. Is this possible?
I have an oracle database.
You can declare both columns to be nullable and create a check constraint that ensures that exactly one of the two columns is not NULL.
For example, if I have separate tables for professors and for lecturers and I wanted to model classes such that a class can either have a professor or a lecturer but not both, I can do something like this
SQL> ed
Wrote file afiedt.buf
1 create table professor(
2 professor_id number primary key
3* )
SQL> /
Table created.
SQL> create table lecturer(
2 lecturer_id number primary key
3 );
Table created.
SQL> create table class(
2 class_id number primary key,
3 lecturer_id number references lecturer( lecturer_id ),
4 professor_id number references professor( professor_id ),
5 check( (lecturer_id is null and professor_id is not null) or
6 (lecturer_id is not null and professor_id is null) )
7 );
Table created.
SQL> insert into professor values( 1 );
1 row created.
SQL> insert into lecturer values( 20 );
1 row created.
SQL> insert into class values( 1, 20, null );
1 row created.
SQL> insert into class values( 2, null, 1 );
1 row created.
SQL> insert into class values( 3, 20, 1 );
insert into class values( 3, 20, 1 )
*
ERROR at line 1:
ORA-02290: check constraint (SCOTT.SYS_C0014175) violated
Of course, from a data modeling standpoint, it will frequently be a better idea to create a single INSTRUCTOR table with an INSTRUCTOR_TYPE that can be either LECTURER or PROFESSOR and to create the CLASS table with a non-nullable foreign key to the INSTRUCTOR table.
I would create a different table for the new interface requirements. One difference in constraints just leads to another.
Then I'd think about whether I should create a view, and let the application software read from the view instead of from the base table(s).
No, it isn't possible. You can put together what would amount to a tertiary key table that links out to either one or the other tables, but it is very messy and brittle.
Instead, I would highly suggest you revisit your integration strategy, since it seems that your current one isn't able to flex the way you need it to.

Oracle cyclic foreign key references issues

I've been racking my brain trying to come up with a solution to this.
For a database class, I need to implement the following:
Table HUSBANDS: (Name Varchar2(10)) (Wife Varchar2(10))
Table WIVES: (Name Varchar2(10)) (Husband Varchar2(10))
and using Oracle constraints, enfore the following rules:
No two husbands can have the same name
No two wives can have the same name
Every wife must have one and only one husband
Every husband must have one and only one wife
So far, I have implemented the table in Oracle SQL:
create table husbands(
name varchar2(10) not null
, wife varchar2(10) not null
);
create table wives(
name varchar2(10) not null
, husband varchar2(10) not null
);
I'm pretty sure I have solved points 1 and 2 using correct primary keys:
alter table husbands
add constraint husbands_pk
primary key(name);
alter table wives
add constraint wives_pk
primary key(name);
And here is where I'm running into issues. I figured to use foreign keys to implement steps 3 and 4:
alter table husbands
add constraint husbands_fk_wife
foreign key(wife)
references wives(name);
alter table wives
add constraint wives_fk_husband
foreign key(husband)
references husbands(name);
Now the test case my professor is using is to be able to add a married couple to the database. The problem I am having is how to do this using only constraints. If I wanted to add Jack and Jill as a married couple, one cannot add the husband until the wife is added. the wife cannot be added until a husband is added.
I think my problem is using foreign keys. A check constraint might work in this situation, but I cannot conceptualize how it would work.
An alternative to deferrable constraints is a third table of (husband, wife) with two unique constraints (one on husband, one on wife), and have referential integrity constraints between that and the husbands table and wifes table. The wife/husband columns on the husbands/wifes tables would be redundant and should be dropped.
PS. Should it be WIVES rather than WIFES ?
The need to use deferrable constraints is often a pointer to design problems. Certainly this data model is not a good one: it is not properly normalised. A normalised solution would look like this:
PERSON
------
ID number
NAME varchar2(30)
PRIMARY KEY (ID)
MARRIED_COUPLE
--------------
PARTNER_1 number
PARTNER_2 number
PRIMARY KEY (PARTNER_1, PARTNER_2)
FOREIGN KEY (PARTNER_1) REFERENCES (PERSON.ID)
FOREIGN KEY (PARTNER_2) REFERENCES (PERSON.ID)
This has the added advantage of supporting civil partnerships :) If you want to discourage bigamy then you could put unique keys on PARTNER_1 or PARTNER_2.
It is trickier to model cultures where polygyny or polyandry is permitted.
edit
What David is objecting to (in the comments) is this:
SQL> create table married_couple (partner_1 number, partner_2 number)
2 /
Table created.
SQL> alter table married_couple add primary key (partner_1, partner_2)
2 /
Table altered.
SQL> insert into married_couple values (1, 2)
2 /
1 row created.
SQL> insert into married_couple values (2,1)
2 /
1 row created.
SQL>
It's a valid point but it is resolvable. For instance, with Oracle I can create a unique function-based to enforce uniqueness of permutations.
SQL> delete from married_couple
2 /
2 rows deleted.
SQL> create unique index mc_uidx on married_couple
2 (greatest(partner_1, partner_2),least(partner_1, partner_2))
3 /
Index created.
SQL> insert into married_couple values (1, 2)
2 /
1 row created.
SQL> insert into married_couple values (2,1)
2 /
insert into married_couple values (2,1)
*
ERROR at line 1:
ORA-00001: unique constraint (APC.MC_UIDX) violated
SQL>
To avoid polygamy we can use a similar trick. We don't want this:
SQL> insert into married_couple values (1,3)
2 /
1 row created.
So, we need two indexes:
SQL> delete from married_couple where partner_2 = 3;
1 row deleted.
SQL> create unique index mc1_uidx
2 on married_couple (greatest(partner_1, partner_2))
3 /
Index created.
SQL> create unique index mc2_uidx
2 on married_couple (least(partner_1, partner_2))
3 /
Index created.
SQL> insert into married_couple values (3, 1)
2 /
insert into married_couple values (3, 1)
*
ERROR at line 1:
ORA-00001: unique constraint (APC.MC2_UIDX) violated
SQL>
To those who think it's cheating to solve a data modelling issue with an implementation trick, I plead "Guilty as charged" but I have had a long and trying day of it.
Study deferrable constraints (not a new type, just a param to the existing ones), so far you did good.
Deferrable constraints are the right way to do it. Interestingly, there is an alternative way however -- with your setup and Oracle 10gR2:
SQL> CREATE OR REPLACE TRIGGER husband_wife_trg AFTER INSERT ON husbands
2 FOR EACH ROW
3 BEGIN
4 INSERT INTO wives VALUES (:new.wife, :new.name);
5 END;
6 /
Trigger created
SQL> INSERT INTO husbands VALUES ('Husband A', 'Wife B');
1 row inserted
SQL> SELECT * FROM wives;
NAME HUSBAND
---------- ----------
Wife B Husband A
I don't like putting transactional logic into triggers, but if you follow this path you don't need deferrable constraints.
Silly idea - why not just have a single table "Couples" with columns "Husband_Name" and "Wife_Name" that each have a unique constraint? Seems to me like this satisfies all the requirements. :)
1)setAutoCommit() as false
2)Inserts record into both table in one Unit Of Work.
3)commit
You need a third table, not only for fixing this, but also for properly handling polygamy/bigamy which is legal in over 40 countries of the world.
Sorry - most answers are not adressing the exact problem at hand:
"MUST HAVE ONE AND ONLY ONE"
This essentially implies: YOU CAN NOT INSERT A SINGLE PERSON into the Database!!!
*Because a single Person would not have exactly one partner!
So the only valid solutions are:
Deferrable Constraints: Easy as it can be - just mark your Constraints deferrable and then insert a wife and a husband and it will only check for integrity after commit (I don't know what people are complaining about - this is not cheating or strange... It is common practice and the only way in many commercial cases!!!)
INSERT ALL - at least with newer Oracle Versions you can use the INSERT ALL Statement, which will insert into multiple tables at once. So you can write a single "Insert wife and hsuband" which is a pissibility for many use-cases.
Trigger: In this special case a Trigger would do the trick - but as soon as you have additional attributes it wouldn't work anymore...
But all other answers were simply wrong for the proposed problem: Two objects with a mandatory 1 to 1 connection