I have the following scenario: A 'phone' child table can serve several parent tables through join tables, as follows:
CREATE TABLE phone (
id BIGINT AUTO_INCREMENT,
number VARCHAR(16) NOT NULL,
type VARCHAR(16) NOT NULL,
PRIMARY KEY(id)
);
CREATE TABLE employee_phone (
id BIGINT AUTO_INCREMENT,
employee BIGINT NOT NULL,
phone BIGINT NOT NULL,
PRIMARY KEY(id),
CONSTRAINT empl_phone_u_phone UNIQUE(phone),
CONSTRAINT empl_phone_fk_employee
FOREIGN KEY(employee)
REFERENCES employee(id) ON DELETE CASCADE,
CONSTRAINT empl_phone_fk_phone
FOREIGN KEY(phone)
REFERENCES phone(id) ON DELETE CASCADE
);
Let Alice and Bob live in the same house and be employees of the same company. HR has two phone numbers registered for Alice whereas they have only one for Bob, the number of the house's landline. Is there a way to enforce at database level that a phone number (number-type) cannot be repeated for the same employee (or supplier, or whatever parent appears later), using this configuration? Or will I have to take care of such restrictions in the application layer? I'd rather not use triggers or table denormalization (as seen in related questions on the site such as this one, which work with IDs, not with other fields), but I'm open to do so if there's no alternative. I'm using MySQL. Thanks for your attention.
If I understand correctly, you just want unique constraints on the junction tables:
alter table employee_phone add constraint unq_employeephone_employee_phone unique (employee, phone);
This will prevent duplicates for a given employee or (with the equivalent constraint) supplier.
If you want all phone numbers to be unique in the phone table, then just put a unique constraint/index on phone:
alter table phone add constraint unq_phone_phone unique (phone);
(you might want to include the type as well).
If you try to add a duplicate phone, the code will return an error.
Related
I am working on a data model where I need to store Employee's basic details and his rating of skillsets in MySQL database.
The number of skillsets for each employee is more than 100.
So the information I need to store is as following:
Employee ID, Name , Department , Contact info, Skillset1,Skillset2,Skillset3, ... , Skillset115
Is creating one table with approximately 120 columns is good approach?
If not, what is the best practice to deal with this kind of requirement.
No. You should have a separate table with one row per employee and per skill:
create table employeeSkills (
employeeSkillId int auto_increment primary key,
employeeId int not null,
skill varchar(255),
constraint fk_employeeSkills_employeeid foreign key (employeeId) references employees(employeeId)
);
In fact, you should really have two extra tables. The skills themselves should be stored in a separate table and the above should really be:
create table employeeSkills (
employeeSkillId int auto_increment primary key,
employeeId int not null,
skillId int,
constraint fk_employeeSkills_employeeid foreign key (employeeId) references employees(employeeId),
constraint fk_employeeSkills_skillid foreign key (skillId) references skills(skillId)
);
This type of table is called a "junction table", and is common in any properly constructed data model.
You need to create two tables that would handle the skills and the assigned skill for each employee.
This would give you a proper order in your database and also will extend your options in the future. It'll be better in search, add and assign skills to each employee. It's even more organized and would be able to be expanded easily such as adding skills category and sub-category.
The two tables schema should be something like this :
CREATE TABLE Skills (
Skill_ID INT NOT NULL AUTO_INCREMENT,
Skill_Description VARCHAR(250),
PRIMARY KEY (`Skill_ID`)
);
CREATE TABLE EmpolyeeSkills (
ES_ID INT NOT NULL AUTO_INCREMENT,
Skill_ID INT,
Employee_ID INT,
PRIMARY KEY (`ES_ID`),
CONSTRAINT FK_EMPLOYEEID FOREIGN KEY (Employee_ID) REFERENCES Employees(Employee_ID),
CONSTRAINT FK_SKILLID FOREIGN KEY (Skill_ID) REFERENCES Skills(Skill_ID)
);
The Skills table will assign an ID for each skill, and it'll be in a separate table. This will make you have a unique skills list, there won't be any redundancy. Then, you'll use EmployeeSkills to save the assigned skills on each Employee_ID. Which you can use it later on to join it with other records.
The FOREIGN KEY on Employee_ID and Skill_ID will help you in monitoring the skills between them.
The ES_ID primary key for EmpolyeeSkills will be an additional advantage that can be helpful in the future. For instance, if you want to know the latest skill that has been assigned, then your faster approach will be getting the last ES_ID as it's an AUTO_INCREMENT. This is just one advantage from tons of others.
I have three tables:
CREATE TABLE Address (
ResidentID CHAR(5) NOT NULL,
Location varchar(255) NOT NULL,
KEY ResidentID(ResidentID)
);
CREATE TABLE Customer (
CustomerID CHAR(5) NOT NULL,
ContactName varchar(40) NOT NULL,
PRIMARY KEY (CustomerID)
);
CREATE TABLE Supplier (
SupplierID CHAR(5) NOT NULL,
SupplierName varchar(40) NOT NULL,
PRIMARY KEY (SupplierID)
);
I want to store CustomerID and SupplierID in the Address.ResidentID field with using of foreign keys:
ALTER TABLE Address ADD CONSTRAINT fk_CustomerID1 FOREIGN KEY(ResidentID) REFERENCES Customer(CustomerID);
ALTER TABLE Address ADD CONSTRAINT fk_SupplierID1 FOREIGN KEY(ResidentID) REFERENCES Supplier(SupplierID);
But second 'ALTER TABLE' raises Error: relation already exists
Any suggestions?
Data example:
CustomerID ContactName
C0001 Den
SupplierID ContactName
S0001 John
So Address table should contains:
ResidentID Location
C0001 Alaska
S0001 Nevada
You need to either reference addresses from the Customer / Supplier (if they only have one) or two different columns.
The reason you see in this SQLFiddle You cannot INSERT the required columns into the Address table if the ResidentID references BOTH tables. You could only insert lines that would match the contents of Customer AND Supplier but you want an OR connection that you can't create that way.
(Note: In my solutions I assume addresses to be optional. As Tom pointed out in the comments that may not be what you wanted, or expected. Make sure to mark the FK Columns in the first solution as NOT NULL if you want addresses to be mandatory, its more complicated for the second one. You have to mind the correct insertion order then.)
Either:
CREATE TABLE Address (
AddressID CHAR(5) NOT NULL,
Location varchar(255) NOT NULL,
PRIMARY KEY (AddressID)
);
CREATE TABLE Customer (
CustomerID CHAR(5) NOT NULL,
AddressID CHAR(5),
ContactName varchar(40) NOT NULL,
PRIMARY KEY (CustomerID)
);
CREATE TABLE Supplier (
SupplierID CHAR(5) NOT NULL,
AddressID CHAR(5),
SupplierName varchar(40) NOT NULL,
PRIMARY KEY (SupplierID)
);
ALTER TABLE Customer ADD CONSTRAINT fk_AddressID_Cust FOREIGN KEY(AddressID) REFERENCES Address(AddressID);
ALTER TABLE Supplier ADD CONSTRAINT fk_AddressID_Supp FOREIGN KEY(AddressID) REFERENCES Address(AddressID);
or
CREATE TABLE Address (
CustomerID CHAR(5),
SupplierID CHAR(5),
Location varchar(255) NOT NULL,
PRIMARY KEY (CustomerID, SupplierID)
);
CREATE TABLE Customer (
CustomerID CHAR(5) NOT NULL,
ContactName varchar(40) NOT NULL,
PRIMARY KEY (CustomerID)
);
CREATE TABLE Supplier (
SupplierID CHAR(5) NOT NULL,
SupplierName varchar(40) NOT NULL,
PRIMARY KEY (SupplierID)
);
ALTER TABLE Address ADD CONSTRAINT fk_CustomerID1 FOREIGN KEY(CustomerID) REFERENCES Customer(CustomerID);
ALTER TABLE Address ADD CONSTRAINT fk_SupplierID1 FOREIGN KEY(SupplierId) REFERENCES Supplier(SupplierID);
The approach you're trying is (a) not possible and (b) undesirable even if it was possible.
The best approach is to have a CustomerAddress table and a SupplierAddress table, each with a single FK to the matching base table; or if you must, a cross-reference table with appropriate constraints.
If your motivation for having a single Address table was code reuse, you can still do that ... think in terms of a template xxxAddress table design that can refer to any base xxx table. You can write non-database code that treats the base table name as a parameter and then could handle any number of xxxAddress tables as you add more base tables over time.
Or if your motivation for having a single Address table was to simplify reporting, you can always create a view or stored proc that returns a union of all such tables + an added field to indicate the base table for each address row.
Angelo I am revising this a bit based on your comment ---
Angelo, I ran your sample code in a local MySQL instance (not SQLFiddle) and observed an error.
I was surprised (you learn something every day) that MySQL did allow two foreign key constraints to be defined on the same field; however when you attempt to insert data, when trying to point the FK to the Customer table, I get an error saying a foreign key constraint fails referencing the Supplier table; and vice versa for the insert trying to point the FK to the Supplier table.
So my revised statement is (a) it is possible to create the hydra-headed FK in at least some DBMSs -- verified in MySQL, MS SQL Server and Oracle -- although (b) this only makes sense to use when the foreign key can refer to the same logical entity by ID across multiple tables (e.g. to ensure there is a corresponding record in all required tables, for example); and (c) if used to refer to multiple tables where the primary key is NOT the same logical entity, only works if by chance the same primary key value just happens to exist in all referenced tables, which is likely to lead to subtle, hard-to-find errors.
In other words, your example would work when attempting to insert a record referring to Customer ID=3 only if there was also a Supplier ID=3, which are really logically unrelated.
So my slightly revised answer to the OP is, what you're trying to do is not possible (or logical) when the foreign key is referring to different ENTITIES, as in the OP example of Customers and Suppliers.
I have been trying to create a foregin key with nbrseats but I i get the error 1005 all the time.
CAn someone help me!?
create table theater (
name varchar(30) primary key,
nbrseats int not null
) ENGINE=INNODB;
create table reservation (
nbr integer auto_increment,
users_username varchar(30),
cinemashow_showdate date,
movies varchar(30),
nbrseats int not null,
primary key (nbr),
foreign key (nbrseats) references theater(nbrseats),
foreign key (users_username) REFERENCES users(username)
on delete cascade,
foreign key (cinemashow_showdate, movies) references cinemashow(showdate, movie_title)
on delete cascade
) ENGINE=INNODB;
In order to be a FOREIGN KEY in another table, you must have an index created on theater.nbrseats. And in order to be able to reference a specific row reliably, it should therefore be a UNIQUE index. Otherwise, if you have duplicate values, the referencing table won't be able to discern which row it references. Even though InnoDB will allow you to create the relationship on a non-unique index, it is likely not the behavior you are looking for.
See this question for more info on that bit.
create table theater (
name varchar(30) primary key,
nbrseats int not null,
UNIQUE INDEX `idx_nbrseats` (nbrseats)
) ENGINE=INNODB;
The same will be true of the other FOREIGN KEY definitions in your table reservation, though we do not see their referenced tables posted here. The rules are:
The referenced column must be indexed (independently of any other compound indexes on it)
The referencing column must have exactly the same data type.
This kind of calls into question your design, however. If you are attaching a number of seats to a reservation, will the reservation number of seats exactly match the number available in the theater? Also this means that you could not have 2 theaters with the same number of seats.
You may need to reconsider your design here, and perhaps create a FOREIGN KEY that references theater.name instead of theater.nbrseats.
Situation is this:
I have a table for staff and another one for course.
In course I will have a column called coordinator which will refer to the staff_id.
This is because some staff are coordinators and some are not, etc etc.
Staff_id is an IDENTITY hence it is auto-incrementing and I have to make sure the in the course table, it won't follow this auto-increment.
I'm just unsure how do I implement the check constraint for this case.
I have to make sure the in the course table, it won't follow this auto-increment.
Do you just mean that you don't want the coordinator field in the course table to auto-increment? It won't unless you tell it to.
For example these two tables would work fine:
CREATE TABLE Staff
(
`staff_id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(64),
PRIMARY KEY (`staff_id`)
)
CREATE TABLE Courses
(
`course_id` INT NOT NULL AUTO_INCREMENT,
`coordinator_id` INT NOT NULL,
`coursename` VARCHAR(64),
PRIMARY KEY (`course_id`),
INDEX `coordid` (`coordinator_id`)
)
Of course, as GarethD says, you can then additionally add constraints to guarantee that the coordinator_id is correctly filled in and the corresponding record exists in staff.
If I'm understanding your requirements properly you can add a foreign key constraint to the table so that it references itself:
ALTER TABLE Staff
ADD CONSTRAINT FK_Staff_Coordinator FOREIGN KEY (Coordinator)
REFERENCES Staff (StaffID)
ADENDUM
I think I had misunderstood your requirements, I missed the part about a course table. So now I am thinking that the course table needs to link back to the staff table, in which case the command would be:
ALTER TABLE Course
ADD CONSTRAINT FK_Course_Coordinator FOREIGN KEY (Coordinator)
REFERENCES Staff (StaffID)
You are of course free to implement your own naming convention for constraints.
I've two tables one about Customers and the second one is about their Accounts as 'Customer_Account_Information'. we know that one customer can have only one account, so I'm trying to enforce one to one relationship, but i don't know the procedure/syntax in mySql or mySqlyog.
is there any one who can help me?
You should create a Foreign Key contraint on table Accounts to table Customers using CustomerID.
Have a look at FOREIGN KEY Constraints
Also then make this a Unique Column in table Accounts
Have a look at MySQL foreign keys - how to enforce one-to-one across tables?
You could also use the same CustomerID from table Customers as PRIMARY KEY column in table Accounts as PRIMARY KEY.
A simple implementation
This is not a perfect solution but it is easy to understand with a little bit of experience with mysql and referential integrity.
You need a FOREIGN KEY in customers to refer to customer_account_inforamtion. Since the account_id in customers is the only way to join the tables, no customer can have more than one record in customer_account_information.
In the sample definitions below, I've included a FOREIGN KEY CONSTRAINT which causes the customer to be deleted when their account information is deleted. You may or may not want/need this.
CREATE TABLE customer_account_information (
id int(11) NOT NULL auto_increment,
some_attribute varchar(20) NOT NULL,
another_attribute varchar(30) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE customers (
id int(11) NOT NULL auto_increment,
account_id int(11) NOT NULL,
firstname varchar(30) NOT NULL,
surname varchar(30) NOT NULL,
PRIMARY KEY (id),
INDEX account_id (account_id),
CONSTRAINT account_id FOREIGN KEY (account_id) REFERENCES customer_account_information (id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
The account_id in customers is the foreign key which refers to a record in customer_account_information
Alternative implementation
It might be an idea to revisit the design of the two tables. If each customer really can have only one account then perhaps all of the attributes of each can be merged into one table. Yes this business rule may change in the future but it would be a better idea to defer this work until really necessary - don't over engineer the solution today.