Foreign Key Used in Composite Primary Key - sql

Is it possible to use a composite foreign key as a piece of a table's composite primary key?
For instance, let's say I have two tables:
CREATE TABLE DB.dbo.Partners
(
CONSTRAINT pk_Partners_Id
PRIMARY KEY (Name, City, State, Country, PostalCode),
Name VARCHAR(100) NOT NULL,
Address1 VARCHAR(100),
Address2 VARCHAR(100),
Address3 VARCHAR(100),
City VARCHAR(150) NOT NULL,
State CHAR(2) NOT NULL,
Country CHAR(2) NOT NULL,
PostalCode VARCHAR(16) NOT NULL,
Phone VARCHAR(20),
Fax VARCHAR(20),
Email VARCHAR(256)
)
... and then in a second table, I would like to reference the foreign key in the second table's primary key:
CREATE TABLE DB.dbo.PartnerContacts
(
CONSTRAINT pk_PartnerContacts_Id
PRIMARY KEY (fk_PartnerContacts_PartnerId, FirstName, LastName, PhoneNumber, Email),
CONSTRAINT fk_PartnerContacts_PartnerId
FOREIGN KEY REFERENCES Partners(Name, City, State, Country, PostalCode),
FirstName VARCHAR(75) NOT NULL,
MiddleName VARCHAR(75),
LastName VARCHAR(75) NOT NULL,
PhoneNumber VARCHAR(20) NOT NULL,
MobileNumber VARCHAR(20),
FaxNumber VARCHAR(20),
Email VARCHAR(256) NOT NULL,
MailTo VARCHAR(100),
Address1 VARCHAR(100),
Address2 VARCHAR(100),
Address3 VARCHAR(100),
City VARCHAR(150),
State CHAR(2),
Country CHAR(2),
PostalCode VARCHAR(16)
)
Is there any way that I can do that? Yes, it might be easier to just simply use IDENTITY columns in these tables but if I can define an actual relationship without an IDENTITY I would like to do that.
EDIT:
I wanted to provide the final, working SQL. Thanks to everyone who answered!
CREATE TABLE DB.dbo.Partners
(
CONSTRAINT pk_Partners_Id
PRIMARY KEY (Name, City, State, Country, PostalCode),
Id INT NOT NULL UNIQUE IDENTITY(1, 1),
Name VARCHAR(100) NOT NULL,
Address1 VARCHAR(100),
Address2 VARCHAR(100),
Address3 VARCHAR(100),
City VARCHAR(150) NOT NULL,
State CHAR(2) NOT NULL,
Country CHAR(2) NOT NULL,
PostalCode VARCHAR(16) NOT NULL,
Phone VARCHAR(20),
Fax VARCHAR(20),
Email VARCHAR(256)
)
CREATE TABLE DB.dbo.PartnerContacts
(
CONSTRAINT pk_PartnerContacts_Id
PRIMARY KEY
(PartnerId, FirstName, LastName, PhoneNumber, Email),
PartnerId INT NOT NULL CONSTRAINT fk_PartnerContacts_PartnerId FOREIGN KEY REFERENCES Partners(Id),
FirstName VARCHAR(75) NOT NULL,
MiddleName VARCHAR(75),
LastName VARCHAR(75) NOT NULL,
PhoneNumber VARCHAR(20) NOT NULL,
MobileNumber VARCHAR(20),
FaxNumber VARCHAR(20),
Email VARCHAR(256) NOT NULL,
MailTo VARCHAR(100),
Address1 VARCHAR(100),
Address2 VARCHAR(100),
Address3 VARCHAR(100),
City VARCHAR(150),
State CHAR(2),
Country CHAR(2),
PostalCode VARCHAR(16)
)

You probably need to specify the columns that are supposed to match.
CONSTRAINT fk_PartnerContacts_PartnerId
FOREIGN KEY (columns that correspond to referenced columns)
REFERENCES Partners (Name, City, State, Country, PostalCode),
So you need to provide the five column names whose values are supposed to match the values of {Name, City, State, Country, PostalCode} in the table "Partners". i'm pretty sure youcan't do that with your current structure. You won't be able to match "Name". I think you're looking for something along these lines.
CREATE TABLE DB.dbo.PartnerContacts (
-- Start with columns that identify "Partner".
partner_name VARCHAR(100) NOT NULL,
partner_city VARCHAR(150) NOT NULL,
partner_state CHAR(2) NOT NULL,
partner_country CHAR(2) NOT NULL,
partner_postcode VARCHAR(16) NOT NULL,
CONSTRAINT fk_PartnerContacts_PartnerId
FOREIGN KEY (partner_name, partner_city, partner_state, partner_country, partner_postcode)
REFERENCES Partners (Name, City, State, Country, PostalCode),
FirstName VARCHAR(75) NOT NULL,
MiddleName VARCHAR(75),
LastName VARCHAR(75) NOT NULL,
PhoneNumber VARCHAR(20) NOT NULL,
MobileNumber VARCHAR(20),
FaxNumber VARCHAR(20),
Email VARCHAR(256) NOT NULL,
MailTo VARCHAR(100),
Address1 VARCHAR(100),
Address2 VARCHAR(100),
Address3 VARCHAR(100),
City VARCHAR(150),
State CHAR(2),
Country CHAR(2),
PostalCode VARCHAR(16),
CONSTRAINT pk_PartnerContacts_Id
PRIMARY KEY (partner_name, partner_city, partner_state, partner_country, partner_postcode,
FirstName, LastName, PhoneNumber, Email)
);

Yes it is possible and is generally considered best DB design practice, but practically, an ID column is just easier to deal with. Think of join tables, their primary key is a composite of two foreign keys. There is no difference to using multiple foreign keys as part of a composite primary key.

Yes, that is definitely possible. We do have instances where we have a composite foreign key that is a part of the composite primary key of other table.
Let's simplify the use case little bit for the below example.
Say we have a table test1 having a composite primary key (A, B)
Now we can have a table say test2 having primary key (P, Q, R) where in (P,Q) of test2 references (A,B) of test1.
I ran the following script in the MySql database and it works just fine.
CREATE TABLE `test1` (
`A` INT NOT NULL,
`B` VARCHAR(2) NOT NULL,
`C` DATETIME NULL,
`D` VARCHAR(45) NULL,
PRIMARY KEY (`A`, `B`));
CREATE TABLE `test2` (
`P` INT NOT NULL,
`Q` VARCHAR(2) NOT NULL,
`R` INT NOT NULL,
`S` DATETIME NULL,
`T` VARCHAR(8) NULL,
PRIMARY KEY (`P`, `Q`, `R`),
INDEX `PQ_idx` (`P`,`Q` ASC),
CONSTRAINT `PQ`
FOREIGN KEY (`P`, `Q`)
REFERENCES `test1` (`A`,`B`)
ON DELETE CASCADE
ON UPDATE CASCADE);
In the above mentioned case, the database is expecting the combination of (A,B) to be unique and it is, being a primary key in test1 table.
But if you try to do something like following, the script would fail. The database would not let you create the test2 table.
CREATE TABLE `test2` (
`P` INT NOT NULL,
`Q` VARCHAR(2) NULL,
`R` DATETIME NULL,
`S` VARCHAR(8) NULL,
`T` VARCHAR(45) NULL,
INDEX `P_idx` (`P` ASC),
INDEX `Q_idx` (`Q` ASC),
CONSTRAINT `P`
FOREIGN KEY (`P`)
REFERENCES `test1` (`A`)
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT `Q`
FOREIGN KEY (`Q`)
REFERENCES `test1` (`B`)
ON DELETE CASCADE
ON UPDATE CASCADE);
In the above mentioned case database would expect the column A to be unique individually and the same follows for column B. It does not matter if combination of (A,B) is unique.

Related

How to implement a double constraint in SQL Server?

I am designing a database project that holds records for an alumni association. My goal was to make sure that the names in the awards table only come from those who have been nominated in the alumni table (where Award_Nominated acts as a Boolean). I have tried using two booleans, one for nomination and another for winning, but that still leaves a possibility for a logical error, i.e. one can be an award winner without getting nominated. My question is how can I make sure that the award winner names only come from the alumni name values with Award_nominated as true.
The relevant tables are declared below:
CREATE TABLE Alumni
(
Alumni_ID int NOT NULL IDENTITY(1,1),
First_Name varchar(20) UNIQUE NOT NULL,
Last_Name varchar(20) UNIQUE NOT NULL,
Graduation_Year int NOT NULL,
Course_taken varchar(255) NOT NULL,
Award_Nominated bit,
Phone_Number int NOT NULL,
Email_Address varchar(255) NOT NULL,
PRIMARY KEY (Alumni_ID, First_Name, Last_Name)
);
CREATE TABLE Awards
(
Award_year int NOT NULL,
Chapters varchar(255),
Award_Winner_First_Name varchar(20) NOT NULL,
Award_Winner_Last_Name varchar(20) NOT NULL,
FOREIGN KEY (Award_Winner_First_Name) REFERENCES Alumni(First_Name),
FOREIGN KEY (Award_Winner_Last_Name) REFERENCES Alumni(Last_Name),
Award_purpose varchar(255) NOT NULL
);
Couple points:
simplify the Alumni table's primary key to be just the Alumni_ID
Don't make first and last name unique - really bad idea ....
reference the Awards table to the Alumni table solely based on the Alumni_ID - not on first and last name ....
don't duplicate the first and last name of the alumni into the Awards table -
if need be, you can get it be a JOIN to the Alumni table
I'd go with this:
CREATE TABLE dbo.Alumni
(
Alumni_ID int NOT NULL IDENTITY(1,1)
CONSTRAINT PK_Alumni PRIMARY KEY CLUSTERED,
First_Name varchar(20) NOT NULL,
Last_Name varchar(20) NOT NULL,
Graduation_Year int NOT NULL,
Course_taken varchar(255) NOT NULL,
Award_Nominated bit,
Phone_Number int NOT NULL,
Email_Address varchar(255) NOT NULL
);
CREATE TABLE dbo.Awards
(
Award_year int NOT NULL,
Alumni_ID int NOT NULL
CONSTRAINT FK_Award_Alumni REFERENCES dbo.Alumni(Alumni_ID),
Chapters varchar(255),
Award_purpose varchar(255) NOT NULL
);
Don't duplicate the names in the Awards table. Reference the Alumni ID and join.
CREATE TABLE Alumni (
Alumni_ID int not null identity(1,1),
-- It's possible to have two alumni with the same name.
-- And also to have names longer than 20 characters.
First_Name varchar(255) not null,
Last_Name varchar(255) not null,
Graduation_Year int not null,
Course_taken varchar(255) not null,
Phone_Number int not null,
Email_Address varchar(255) not null,
-- Just the Alumni_ID is sufficient
PRIMARY KEY (Alumni_ID)
);
CREATE TABLE Awards (
Award_year int not null,
Chapters varchar(255),
-- Only reference the ID
Alumni_ID int not null,
FOREIGN key (Alumni_ID) REFERENCES Alumni(Alumni_ID),
Award_purpose varchar(255) not null
);
Then join with Alumni to get their names.
select First_Name, Last_Name, Awards.*
from Awards
join Alumni on Awards.Alumni_ID = Alumni.Alumni_ID

Error ORA-00904 in SQL developer. Seemly no fix online that works that I can find while creating a table

I have seen many posts regarding the seemingly infamous ORA-00904 yet nothing seems to be helping. I am creating 2 tables (for now, more to come later) which are as follows;
CREATE TABLE Employee(
EmployeeID int NOT NULL,
PRIMARY KEY(EmployeeID),
customerID INT NOT NULL,
CONSTRAINT employee_customer_fk FOREIGN KEY (customerID) REFERENCES Customer(CustomerID),
LastName CHAR(20) NOT NULL,
MiddleInitial CHAR(1),
FirstName CHAR(20) NOT NULL,
Region CHAR(20) NOT NULL,
DateOfHire VARCHAR(20) NOT NULL,
Skill VARCHAR(50) NOT NULL
);
and
CREATE TABLE Customer(
CustomerID INT NOT NULL PRIMARY KEY,
ProjectID INT NOT NULL,
FOREIGN KEY(ProjectID) REFERENCES Project(ProjectID), /*This is yet another table with a foreign key which needs to be sorted*/
CustomerName Char(255) NOT NULL,
PhoneNumber INT NOT NULL,
Region CHAR(255) NOT NULL
);
but every time I run it, I get error;
Error starting at line : 4 in command -
CREATE TABLE Employee(
EmployeeID int NOT NULL,
PRIMARY KEY(EmployeeID),
customerID INT NOT NULL,
CONSTRAINT employee_customer_fk FOREIGN KEY(customerID) REFERENCES Customer(CustomerID),
LastName CHAR(20) NOT NULL,
MiddleInitial CHAR(1),
FirstName CHAR(20) NOT NULL,
Region CHAR(20) NOT NULL,
DateOfHire VARCHAR(20) NOT NULL,
Skill VARCHAR(50) NOT NULL
)
Error report -
ORA-00904: "CUSTOMERID": invalid identifier
00904. 00000 - "%s: invalid identifier"
*Cause:
*Action:
ANY HELP WOULD BE GREATLY APPRECIATED!
Oracle by default converts all names to UpperCase if you don't enquote them. (Edit: At the time of this answer the Foreign Key Constraint in the starting post did contain a quoted column name).
So, your constraint tries to find a column named "customerID" whereas the real name of your column is "CUSTOMERID".
Either enquote your column:
CREATE TABLE Employee(
EmployeeID int NOT NULL,
PRIMARY KEY(EmployeeID),
"customerID" INT NOT NULL,
CONSTRAINT employee_customer_fk FOREIGN KEY ("customerID") REFERENCES customer(CustomerID),
LastName CHAR(20) NOT NULL,
MiddleInitial CHAR(1),
FirstName CHAR(20) NOT NULL,
Region CHAR(20) NOT NULL,
DateOfHire VARCHAR(20) NOT NULL,
Skill VARCHAR(50) NOT NULL
)
Don't use quotes on your FOREIGN KEY constraint:
CREATE TABLE Employee(
EmployeeID int NOT NULL,
PRIMARY KEY(EmployeeID),
customerID INT NOT NULL,
CONSTRAINT employee_customer_fk FOREIGN KEY (customerID) REFERENCES customer(CustomerID),
LastName CHAR(20) NOT NULL,
MiddleInitial CHAR(1),
FirstName CHAR(20) NOT NULL,
Region CHAR(20) NOT NULL,
DateOfHire VARCHAR(20) NOT NULL,
Skill VARCHAR(50) NOT NULL
)
Or use quotes and write it uppercase in your foreign key constraint:
CREATE TABLE Employee(
EmployeeID int NOT NULL,
PRIMARY KEY(EmployeeID),
customerID INT NOT NULL,
CONSTRAINT employee_customer_fk FOREIGN KEY ("CUSTOMERID") REFERENCES customer(CustomerID),
LastName CHAR(20) NOT NULL,
MiddleInitial CHAR(1),
FirstName CHAR(20) NOT NULL,
Region CHAR(20) NOT NULL,
DateOfHire VARCHAR(20) NOT NULL,
Skill VARCHAR(50) NOT NULL
)
As already mentioned, keeping the style consistent is recommended to prevent future problems, so Option 2 would be the easiest solution. Otherwise you need to enquote all columns to be consistent.
/Edit:
I just tried it - this definitely works with Oracle 12C:
CREATE TABLE Customer (
CustomerID INT,
PRIMARY KEY (CustomerID)
)
Followed by:
CREATE TABLE Employee(
EmployeeID int NOT NULL,
PRIMARY KEY(EmployeeID),
customerID INT NOT NULL,
CONSTRAINT employee_customer_fk FOREIGN KEY(customerID) REFERENCES Customer(CustomerID),
LastName CHAR(20) NOT NULL,
MiddleInitial CHAR(1),
FirstName CHAR(20) NOT NULL,
Region CHAR(20) NOT NULL,
DateOfHire VARCHAR(20) NOT NULL,
Skill VARCHAR(50) NOT NULL
)
The referenced table needs to created before the foreign key reference. One method is to create the tables first and then add the foreign key references:
CREATE TABLE Employee (
EmployeeID int NOT NULL,
PRIMARY KEY (EmployeeID),
customerID INT NOT NULL,
LastName CHAR(20) NOT NULL,
MiddleInitial CHAR(1),
FirstName CHAR(20) NOT NULL,
Region CHAR(20) NOT NULL,
DateOfHire VARCHAR(20) NOT NULL,
Skill VARCHAR(50) NOT NULL
);
CREATE TABLE Customer (
CustomerID INT NOT NULL PRIMARY KEY,
ProjectID INT NOT NULL,
-- FOREIGN KEY(ProjectID) REFERENCES Project(ProjectID), /*This is yet another table with a foreign key which needs to be sorted*/
CustomerName Char(255) NOT NULL,
PhoneNumber INT NOT NULL,
Region CHAR(255) NOT NULL
);
alter table Employee add CONSTRAINT employee_customer_fk FOREIGN KEY (customerID) REFERENCES Customer (CustomerID)
Here is a db<>fiddle.
Note that I commented out the foreign key constraint to Project.
Also, in most data models you can just order the table creates -- there are no cycles in the foreign key "linkages". However, because you have not defined Project, I am suggesting this more general approach.

Oracle Problems with Composite Keys

bit confusing question here:
I am trying to create a composite FK from a composite PK. I'll show you my tables in question that I'm having problems with;
CREATE TABLE Weapons (
Weapon_ID VARCHAR(10) NOT NULL,
Weapon_Name VARCHAR(30),
Range_In_Meters INT,
Maximum_Number_Of_Uses INT,
Damage_Factor INT,
Cost INT,
primary key(Weapon_ID));
CREATE TABLE WeaponInventory (
Inventory_ID VARCHAR(30) NOT NULL,
WeaponInventory_ID Varchar(10) NOT NULL,
Weapon_ID VARCHAR(10) REFERENCES Weapons(Weapon_ID) NOT NULL,
primary key(Inventory_ID, WeaponInventory_ID));
CREATE TABLE Avatars (
Avatar_ID VARCHAR(10) NOT NULL,
Avatar_Name VARCHAR(30),
AvA_DOB DATE,
Age VARCHAR(30),
Gender VARCHAR(30),
Strength_Indicated INT,
Hoard INT,
Avatar_Level VARCHAR(30),
Skill VARCHAR(30),
Original_Owner VARCHAR(30),
Family_ID VARCHAR(10) NOT NULL,
Species_ID VARCHAR(10) NOT NULL,
Inventory_ID VARCHAR(30) NOT NULL,
Weapon_ID VARCHAR(10) NOT NULL,
Player_ID VARCHAR(10) NOT NULL,
PRIMARY KEY (Avatar_ID),
FOREIGN KEY (Inventory_ID, Weapon_ID)
REFERENCES WeaponInventory (Inventory_ID, Weapon_ID));
In the table Avatars, I have been trying to create this Foreign key but I'm having no luck. I have errors such as:
ORA-02270: no matching unique or primary key for this column-list
and:
ORA-02270: no matching unique or primary key for this column-list
Any help would be greatly appreciated! I just keep getting confused and ending up in the same place! (:
I think you planned to reference column WeaponInventory_ID (instead of Weapon_ID) in table WeaponInventory:
FOREIGN KEY (Inventory_ID, Weapon_ID)
REFERENCES WeaponInventory (Inventory_ID, WeaponInventory_ID) -- see here
Note that Weapon_ID is not part of the PK in WeaponInventory:
CREATE TABLE WeaponInventory (
Inventory_ID VARCHAR(30) NOT NULL,
WeaponInventory_ID Varchar(10) NOT NULL,
Weapon_ID VARCHAR(10) REFERENCES Weapons(Weapon_ID) NOT NULL,
primary key(Inventory_ID, WeaponInventory_ID) -- Weapon_ID is not part of PK
);
Check this Fiddle.

oracle SQL - ORA-009007 Missing right parenthesis

I have this sql
CREATE TABLE PATIENTS
(
patientID NUMBER NOT NULL,
healthInsID NUMBER,
fname VARCHAR(20) NOT NULL,
minit VARCHAR(15),
lname VARCHAR(30) NOT NULL,
gender CHAR(2),
email VARCHAR(40),
street VARCHAR(40),
postalCode NUMBER,
city VARCHAR(20),
country VARCHAR(20),
PRIMARY KEY (patientID),
FOREIGN KEY (healthInsID) REFERENCES HEALTH_INSURANCES
ON DELETE SET NULL ON UPDATE CASCADE
);
I have no idea why I keep getting this error. I have search a lot but still nothing that can solve it. Any ideas?
Thanks
You have to specify column from HEALTH_INSURANCES table in FOREIGN KEY caluse.
You should also remove ON UPDATE CASCADE. You can use a trigger on update instead of this clause.
CREATE TABLE PATIENTS
(
patientID NUMBER NOT NULL,
healthInsID NUMBER,
fname VARCHAR(20) NOT NULL,
minit VARCHAR(15),
lname VARCHAR(30) NOT NULL,
gender CHAR(2),
email VARCHAR(40),
street VARCHAR(40),
postalCode NUMBER,
city VARCHAR(20),
country VARCHAR(20),
PRIMARY KEY (patientID),
FOREIGN KEY (healthInsID) REFERENCES HEALTH_INSURANCES (healthInsID)
ON DELETE SET NULL
);
More information about constraints

i used the program SQL Fiddle and it keeps telling me that the table doesn't exist,what can i do to fix the two tables referencing each other?

the Staff table references the branch table
CREATE TABLE Staff(
StaffNo VARCHAR(5) NOT NULL,
firstName VARCHAR(15) NOT NULL UNIQUE,
lastName VARCHAR(15) NOT NULL,
position VARCHAR(10) NOT NULL,
salary INTEGER
DEFAULT 3000,
CHECK (salary BETWEEN 3000 AND 25000),
email VARCHAR(25),
branchNo CHAR(6) NOT NULL,
PRIMARY KEY (StaffNo),
FOREIGN KEY (branchNo) REFERENCES Branch (branchNo));
and at the same time the branch table references the Staff table
create table Branch(
branchNo char(6) not null primary key,
street varchar(30) not null,
city varchar(20),
postCode char(5) not null,
ManagerNo varchar(5) not null,
foreign key (ManagerNo) references Staff(StaffNo));
Since your tables reference each other in the Foreign Keys you will get an error on either table creation if the other table has not been created yet. I would suggest that you remove the creation of the FOREIGN KEYs to separate ALTER TABLE statements:
CREATE TABLE Staff(
StaffNo VARCHAR(5) NOT NULL,
firstName VARCHAR(15) NOT NULL UNIQUE,
lastName VARCHAR(15) NOT NULL,
position VARCHAR(10) NOT NULL,
salary INTEGER
DEFAULT 3000,
CHECK (salary BETWEEN 3000 AND 25000),
email VARCHAR(25),
branchNo CHAR(6) NOT NULL,
PRIMARY KEY (StaffNo)
);
create table Branch(
branchNo char(6) not null primary key,
street varchar(30) not null,
city varchar(20),
postCode char(5) not null,
ManagerNo varchar(5) not null
);
alter table staff
add constraint fk1_branchNo foreign key (branchNo) references Branch (branchNo);
alter table branch
add constraint fk1_ManagerNo foreign key (ManagerNo) references Staff (StaffNo);
See SQL Fiddle with Demo
You can remove one reference from one table and keep the other.then you can retrieve data using the remainig reference.Is there any problem with that?