Main organization and problem
I'm working on a database of football matches, this db is organized in the following structure:
Country -> Competition -> Season -> Data
The basic matter of the question is that I'm not pretty sure that my database schema is correct.
Explain the data structure
The main "problem" is that each Data is grouped by a specific season, let's take in consideration the country England:
Country | Competition | Season |
England | Premier League | 2017/2018 |
England | Premier League | 2016/2017 |
England | Premier League | 2015/2016 |
England | Premier League | 2014/2015 |
England | Premier League | 2013/2014 |
as you can see the England have a competition named Premier League which is divided into 5 seasons.
Each competition is divided into rounds, a competition can be formed by a single round, but also by more rounds.
Each round could be divided into groups, this depends on the type of competition, some competitions aren't divided into groups.
Database structure
Based on my explaination of data relationship, I configured a database structure which have the following table:
country: contains all the countries informations available.
competition: contains all the competitions details.
competition_seasons: contains all the competition seasons.
competition_rounds: contains all the rounds available for the competition.
competition_groups: contains all the groups available for the competition.
league_ranking: contains all the ranking position of each team that partecipate on the specific competition.
The database schema is this (I does not have enough rep to display the image you need click on the link):
enter image description here
Database code
-- -----------------------------------------------------
-- Table `mydb`.`country`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`country` (
`id` INT NOT NULL,
`name` VARCHAR(255) NOT NULL,
`link` VARCHAR(255) NOT NULL,
`iso` VARCHAR(45) NOT NULL,
PRIMARY KEY (`id`))
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`competition`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`competition` (
`id` INT NOT NULL,
`country_id` INT NOT NULL,
`name` VARCHAR(255) NOT NULL,
`link` VARCHAR(255) NOT NULL,
PRIMARY KEY (`id`),
INDEX `id_idx` (`country_id` ASC),
CONSTRAINT `FK_country_competition_country_id`
FOREIGN KEY (`country_id`)
REFERENCES `mydb`.`country` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`competition_seasons`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`competition_seasons` (
`season_id` INT NOT NULL,
`competition_id` INT NOT NULL,
`name` VARCHAR(255) NOT NULL,
`create_at` DATETIME NULL,
`update_at` DATETIME NULL,
INDEX `competition_id_idx` (`competition_id` ASC),
PRIMARY KEY (`season_id`),
CONSTRAINT `FK_competition_competition_seasons_competition_id`
FOREIGN KEY (`competition_id`)
REFERENCES `mydb`.`competition` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`competition_groups`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`competition_groups` (
`group_id` INT NOT NULL,
`competition_id` INT NOT NULL,
`round_id` INT NOT NULL,
INDEX `group_id_idx` (`group_id` ASC),
INDEX `competition_id_idx` (`competition_id` ASC),
INDEX `round_id_idx` (`round_id` ASC),
CONSTRAINT `FK_group_competition_groups_group_id`
FOREIGN KEY (`group_id`)
REFERENCES `mydb`.`group` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `FK_competition_competition_groups_competition_id`
FOREIGN KEY (`competition_id`)
REFERENCES `mydb`.`competition` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `FK_round_competition_groups_round_id`
FOREIGN KEY (`round_id`)
REFERENCES `mydb`.`round` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`competition_rounds`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`competition_rounds` (
`competition_id` INT NOT NULL,
`round_id` INT NOT NULL,
INDEX `competition_id_idx` (`competition_id` ASC),
INDEX `round_id_idx` (`round_id` ASC),
CONSTRAINT `FK_competition_competition_rounds_competition_id`
FOREIGN KEY (`competition_id`)
REFERENCES `mydb`.`competition` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `FK_round_competition_rounds_round_id`
FOREIGN KEY (`round_id`)
REFERENCES `mydb`.`round` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`league_ranking`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`league_ranking` (
`id` INT NOT NULL,
`position` INT NULL,
`team_id` INT NULL,
`season_id` INT NULL,
`round_id` INT NULL,
`competition_id` INT NULL,
`group_id` INT NULL,
`played_matches` INT NULL,
`wins` INT NULL,
`draws` INT NULL,
`losses` INT NULL,
`goals_for` INT NULL,
`goals_against` INT NULL,
`goals_difference` INT NULL,
`points` INT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `FK_team_league_ranking_teamd_id`
FOREIGN KEY (`id`)
REFERENCES `mydb`.`team` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `FK_round_league_ranking_round_id`
FOREIGN KEY (`id`)
REFERENCES `mydb`.`round` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `FK_competition_league_ranking_competition_id`
FOREIGN KEY (`id`)
REFERENCES `mydb`.`competition` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `FK_group_league_ranking_group_id`
FOREIGN KEY (`id`)
REFERENCES `mydb`.`group` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
is my database schema correct for store historical season?
Assuming
A competition is composed of 1 or more rounds,
A round is optionally composed of 1 or more groups.
Then I recommend
One table containing one row per 'competition'.
One table containing one row per 'round'. It should contain a competition_id that is a FK to competition.id.
One table containing one row per 'group'. It should contain a round_id that is a FK to round.id.
(Etc.)
Those are examples of doing "1:many" mappings. (Note "0 or more" and "optionally" are merely edge cases of "1:many", and do not require extra effort.)
I say "one table" because "vertical splitting" is rarely unnecessary. Simply put all the attributes for a "competition" in a single table. When some attribute (such as the 'rounds') is repeated, then it cannot be put in the same table.
(The table name competition_rounds, though descriptive, was confusing me.)
A related question... Are all the 'rounds' of a 'competition' played in a single country? I see country_id in competition; I wonder if it should be moved to rounds?
#1. First of all, I didn't understand the usages of "competition_round" table, here you defined this table with two column "competition_id" and "round_id". Anyway, you have defined "competition_id" and "round_id" in "leage_ranking" table, so, I suggest not to use the extra table to store the same data again.
#2. As you mentioned you want to store the data historically, then I am assuming there will be less transaction or not might be. So, I suggest you de-normalize the table. Because the end of the day you are going to analyze the data and going to perform lots of data mining on historical data to provide appropriate business expectations.
#3. Other than this it seems a good data model, as per my experience.
Refer this link for understanding the database design patterns
Related
I have an entity which has NOT NULL requirements based on the group it belongs to. For instance...
There are three types of churches: Buddhist, Muslim, and Christian.
All churches have some common required properties, however, each type of church has additional required properties.
All people have some common required properties, however, they have additional required properties based on the church type they belong to.
People must belong to one and only one church, however, may change their church to any other one of any religion provided that the above rules are met. The "type" of person they are is based on the church type they belong to.
How should entities who's required properties are based on the group which the entity belongs to be modeled? Or given my scenario, how should churches and people be modeled?
This is currently what I am doing, but it does not seem right. For instance, a person can be added before they become a Buddhist, Muslim, or Christian which breaks the rules. Also, a person or church can be more than one type which also breaks the rules.
-- MySQL Script generated by MySQL Workbench
-- 02/10/17 21:41:31
-- Model: New Model Version: 1.0
SET #OLD_UNIQUE_CHECKS=##UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET #OLD_FOREIGN_KEY_CHECKS=##FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET #OLD_SQL_MODE=##SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES';
-- -----------------------------------------------------
-- Schema mydb
-- -----------------------------------------------------
CREATE SCHEMA IF NOT EXISTS `mydb` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ;
USE `mydb` ;
-- -----------------------------------------------------
-- Table `mydb`.`churches`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`churches` (
`idchurches` INT NOT NULL,
`name` VARCHAR(45) NOT NULL,
`address` VARCHAR(45) NOT NULL,
`members` INT NOT NULL,
PRIMARY KEY (`idchurches`))
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`churches_buddhist`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`churches_buddhist` (
`churches_idchurches` INT NOT NULL,
`number_of_buddas_in_church` VARCHAR(45) NOT NULL,
PRIMARY KEY (`churches_idchurches`),
CONSTRAINT `fk_churches_buddhist_churches`
FOREIGN KEY (`churches_idchurches`)
REFERENCES `mydb`.`churches` (`idchurches`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`churches_muslim`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`churches_muslim` (
`churches_idchurches` INT NOT NULL,
`savior` VARCHAR(45) NOT NULL,
PRIMARY KEY (`churches_idchurches`),
CONSTRAINT `fk_churches_muslim_churches1`
FOREIGN KEY (`churches_idchurches`)
REFERENCES `mydb`.`churches` (`idchurches`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`churches_christian`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`churches_christian` (
`churches_idchurches` INT NOT NULL,
`savior` VARCHAR(45) NOT NULL,
`number_of_crosses_in_church` INT NOT NULL,
PRIMARY KEY (`churches_idchurches`),
CONSTRAINT `fk_churches_christian_churches1`
FOREIGN KEY (`churches_idchurches`)
REFERENCES `mydb`.`churches` (`idchurches`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`people`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`people` (
`idpeople` INT NOT NULL,
`name` VARCHAR(45) NOT NULL,
`age` TINYINT NOT NULL,
`race` VARCHAR(45) NOT NULL,
`gender` VARCHAR(45) NOT NULL,
`favoriteVegitable` VARCHAR(45) NOT NULL,
PRIMARY KEY (`idpeople`))
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`buddhists`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`buddhists` (
`people_idpeople` INT NOT NULL,
`WidthOfBelly` BIGINT NOT NULL,
`LevelOfCconsciousness` INT NOT NULL,
`churches_buddhist_churches_idchurches` INT NOT NULL,
PRIMARY KEY (`people_idpeople`),
INDEX `fk_buddhists_churches_buddhist1_idx` (`churches_buddhist_churches_idchurches` ASC),
CONSTRAINT `fk_buddhists_people1`
FOREIGN KEY (`people_idpeople`)
REFERENCES `mydb`.`people` (`idpeople`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_buddhists_churches_buddhist1`
FOREIGN KEY (`churches_buddhist_churches_idchurches`)
REFERENCES `mydb`.`churches_buddhist` (`churches_idchurches`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`muslims`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`muslims` (
`people_idpeople` INT NOT NULL,
`DaysOffTakenForRamadan` INT NOT NULL,
`favoriteMeat` VARCHAR(45) NOT NULL,
`churches_muslim_churches_idchurches` INT NOT NULL,
PRIMARY KEY (`people_idpeople`),
INDEX `fk_muslims_churches_muslim1_idx` (`churches_muslim_churches_idchurches` ASC),
CONSTRAINT `fk_muslims_people1`
FOREIGN KEY (`people_idpeople`)
REFERENCES `mydb`.`people` (`idpeople`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_muslims_churches_muslim1`
FOREIGN KEY (`churches_muslim_churches_idchurches`)
REFERENCES `mydb`.`churches_muslim` (`churches_idchurches`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`christians`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`christians` (
`people_idpeople` INT NOT NULL,
`ChristmasPresentsReceived` INT NOT NULL,
`HolyMarysSaidPerDay` INT NOT NULL,
`favoriteMeat` VARCHAR(45) NOT NULL,
`FavoritePork` VARCHAR(45) NOT NULL,
`churches_christian_churches_idchurches` INT NOT NULL,
PRIMARY KEY (`people_idpeople`),
INDEX `fk_christians_churches_christian1_idx` (`churches_christian_churches_idchurches` ASC),
CONSTRAINT `fk_christians_people1`
FOREIGN KEY (`people_idpeople`)
REFERENCES `mydb`.`people` (`idpeople`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_christians_churches_christian1`
FOREIGN KEY (`churches_christian_churches_idchurches`)
REFERENCES `mydb`.`churches_christian` (`churches_idchurches`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
SET SQL_MODE=#OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=#OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=#OLD_UNIQUE_CHECKS;
One idiom declaratively enforcing disjoint subtypes:
add type discriminator/tag column religion_type to parent and child tables
add superkey UNIQUE NOT NULL (id, religion_type)to parent tables
add FOREIGN (super) KEY (id, religion_type) to child tables, referencing parents
add constraint CHECK( religion_type = 'religion' ) or constant computed column with value religion to child tables
This still doesn't enforce that every parent is a child. From this answer:
One needs triggers to reasonably constrain SQL databases. One uses idioms to get what declarative constraints one can.
Just find the straightforward predicate for each relevant application relationship and give it a table. Here, that's parent and child tables. The constraints follow from the predicates and possible situtations. Declare them to prevent impossible updates. Whenever values in some columns must appear in other columns we declare a FK. You don't have to think about a special case for subtyped entity ids. Certain ids will end up in certain tables because certain things are true of them. It is ultimately their satisfying different predicates that makes things of different "types", rather than vice versa.
I just stumbled across possibility of MySQL foreign key to reference multiple columns. I would like to know what is main purpose of multi-column foreign keys like shown bellow
ALTER TABLE `device`
ADD CONSTRAINT `fk_device_user`
FOREIGN KEY (`user_created_id` , `user_updated_id` , `user_deleted_id`)
REFERENCES `user` (`id` , `id` , `id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION;
My questions are
Is it the same as creating three independent foreign keys?
Are there any pros / cons of using one or another?
What is the exact use-case for this? (main question)
Is it the same as creating three independent foreign keys?
No. Consider the following.
First off, it is not useful to think of it as (id,id,id), but rather (id1,id2,id3) in reality. Because a tuple of (id,id,id) would have no value over just a single column index on id. As such you will see the schema below that depicts that.
create schema FKtest001;
use FKtest001;
create table user
( id int auto_increment primary key,
fullname varchar(100) not null,
id1 int not null,
id2 int not null,
id3 int not null,
index `idkUserTuple` (id1,id2,id3)
);
create table device
( id int auto_increment primary key,
something varchar(100) not null,
user_created_id int not null,
user_updated_id int not null,
user_deleted_id int not null,
foreign key `fk_device_user` (`user_created_id` , `user_updated_id` , `user_deleted_id`)
REFERENCES `user` (`id1` , `id2` , `id3`)
);
show create table device;
CREATE TABLE `device` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`something` varchar(100) NOT NULL,
`user_created_id` int(11) NOT NULL,
`user_updated_id` int(11) NOT NULL,
`user_deleted_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_device_user` (`user_created_id`,`user_updated_id`,`user_deleted_id`),
CONSTRAINT `device_ibfk_1` FOREIGN KEY (`user_created_id`, `user_updated_id`, `user_deleted_id`) REFERENCES `user` (`id1`, `id2`, `id3`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
show indexes from device; -- shows 2 indexes (a PK, and composite BTREE)
-- FOCUS heavily on the `Seq_in_index` column for the above
-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
drop table device;
drop table user;
create table user
( id int auto_increment primary key,
fullname varchar(100) not null,
id1 int not null,
id2 int not null,
id3 int not null,
index `idkUser1` (id1),
index `idkUser2` (id2),
index `idkUser3` (id3)
);
create table device
( id int auto_increment primary key,
something varchar(100) not null,
user_created_id int not null,
user_updated_id int not null,
user_deleted_id int not null,
foreign key `fk_device_user1` (`user_created_id`)
REFERENCES `user` (`id1`),
foreign key `fk_device_user2` (`user_updated_id`)
REFERENCES `user` (`id2`),
foreign key `fk_device_user3` (`user_deleted_id`)
REFERENCES `user` (`id3`)
);
show create table device;
CREATE TABLE `device` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`something` varchar(100) NOT NULL,
`user_created_id` int(11) NOT NULL,
`user_updated_id` int(11) NOT NULL,
`user_deleted_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_device_user1` (`user_created_id`),
KEY `fk_device_user2` (`user_updated_id`),
KEY `fk_device_user3` (`user_deleted_id`),
CONSTRAINT `device_ibfk_1` FOREIGN KEY (`user_created_id`) REFERENCES `user` (`id1`),
CONSTRAINT `device_ibfk_2` FOREIGN KEY (`user_updated_id`) REFERENCES `user` (`id2`),
CONSTRAINT `device_ibfk_3` FOREIGN KEY (`user_deleted_id`) REFERENCES `user` (`id3`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
show indexes from device; -- shows 4 indexes (a PK, and 3 indiv FK indexes)
-- FOCUS heavily on the `Seq_in_index` column for the above
There are 2 sections there. The show indexes from device will show the difference of, in the top part, 2 indexes maintained. In the bottom part, 4 indexes maintained. If for some reason the index tuple in the top part is useful for the system, then that tuple approach is certainly the way to go.
The reason is the following. The tuple exists as a group. Think of it as an instance of a set that has meaning as a group. Compare that to the mere existence of the individual parts, and there is a difference. It is not that the users exist, it is that there is a user row that has that tuple as an existence.
Are there any pros / cons of using one or another?
The pros were described above in the last paragraph: existence as an actual grouping in the user table as a tuple.
They are apple and oranges and used for different purposes.
What is the exact use-case for this? (main question)
A use case would be something that requires the existence of the tuple as a group, as opposed to the existence of the individual items. It is used for what is called compositing. Compositing FK's in particular. See this answer of mine Here as one case.
In short, it is when you want to enforce special hard to think of solutions that require Referential Integrity (RI) at a composited level (groupings) of other entities. Many people think it can't be done so they first think TRIGGER enforcement or front-end Enforcement. Fortunately those use cases can be achieved via the FK Composites thus leaving RI at the db level where it should be (and never at the front-end).
Addendum
Request from OP for a better real life example than the link above.
Consider the following schema:
CREATE SCHEMA testRealLifeTuple;
USE testRealLifeTuple;
CREATE TABLE contacts
( id INT AUTO_INCREMENT PRIMARY KEY,
fullname VARCHAR(100) NOT NULL
-- etc
);
CREATE TABLE tupleHolder
( -- a tuple representing a necessary Three-some validation
-- and vetting to get financing
--
-- If you can't vett these 3, you can't have my supercomputer financed
--
id INT AUTO_INCREMENT PRIMARY KEY,
CEO INT NOT NULL, -- Chief Executive Officer
CFO INT NOT NULL, -- Chief Financial Officer
CIO INT NOT NULL, -- Chief Geek
creditWorthiness INT NOT NULL, -- 1 to 100. 100 is best
-- the unique index is necessary for the device FK to succeed
UNIQUE INDEX `idk_ContactTuple` (CEO,CFO,CIO), -- No duplicates ever. Good for re-use
FOREIGN KEY `fk_th_ceo` (`CEO`) REFERENCES `contacts` (`id`),
FOREIGN KEY `fk_th_cfo` (`CFO`) REFERENCES `contacts` (`id`),
FOREIGN KEY `fk_th_cio` (`CIO`) REFERENCES `contacts` (`id`)
);
CREATE TABLE device
( -- An Expensive Device, typically our Supercomputer that requires Financing.
-- This device is so wildly expense we want to limit data changes
--
-- Note that the GRANTS (privileges) on this table are restricted.
--
id INT AUTO_INCREMENT PRIMARY KEY,
something VARCHAR(100) NOT NULL,
CEO INT NOT NULL, -- Chief Executive Officer
CFO INT NOT NULL, -- Chief Financial Officer
CIO INT NOT NULL, -- Chief Geek
FOREIGN KEY `fk_device_2_tuple` (`CEO` , `CFO` , `CIO`)
REFERENCES `tupleHolder` (`CEO` , `CFO` , `CIO`)
--
-- Note that the GRANTS (privileges) on this table are restricted.
--
);
DROP SCHEMA testRealLifeTuple;
The highlights of this schema come down to the UNIQUE KEY in tupleHolder table, the FK in device, the GRANT restriction (grants not shown), and the fact that the device is shielded from tomfoolery edits in the tupleHolder because of, as mentioned:
GRANTS
That the FK must be honored, so the tupleHolder can't be messed with
If the tupleHolder was messed with (the 3 contacts ids), then the FK would be violated.
Said another way, it is NO WAY the same as the device having an FK based on a single column in device, call it [device.badIdea INT], that would FK back to tupleHolder.id.
Also, as mentioned earlier, this differs from merely having the contacts exist. Rather, it matters that the composition of contacts exists, it is a tuple. And in our case the tuple has been vetted, and has a credit worthiness rating, and the id's in that tuple can't be messed with, after a device is bought, unless sufficient GRANTS allow it. And even then, the FK is in place.
It may take 15 minutes for that to sink in, but there is a Huge difference.
I hope this helps.
I have a table name Service(product, loca, from_agent, to_agent).
product reference Product(pno)
from_agent references Customer(cno) U Driver(drno) U Airline(Ano)
to_agent references Customer(cno) U Driver(drno) U Airline(ano)
cno = Customer Number which is another table name "Customer"which has other details such as name, address etc
drno = Driver Number which is another table name "Driver" which has other details such as name, address etc
ano = Airline Number which is another table name "Airline"which has other details such as depport, depttime,arrtime etc..
would like to write a trigger that will force, the foreign key in the product table to be checked before any changes are made. Assuming local mapping transparency
Please help, I'm just learning about triggers and distribution database
Let's imagine a database for a shop: Items, Products, Brands (For exemple: "Yellow Peugeot bicycle with stripes", referenced as an item of "Peugeot bicycle", as a brand of "Peugeot".
CREATE TABLE IF NOT EXISTS `brands` (
`brandId` int(11) NOT NULL AUTO_INCREMENT,
`brandName` varchar(30) NOT NULL,
PRIMARY KEY (`brandId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;
CREATE TABLE IF NOT EXISTS `items` (
`itemId` int(11) NOT NULL AUTO_INCREMENT,
`generalProductId` int(11) NOT NULL,
PRIMARY KEY (`itemId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `products` (
`productId` int(11) NOT NULL AUTO_INCREMENT,
`productName` varchar(200) NOT NULL,
`productBrand` int(11) NOT NULL,
PRIMARY KEY (`productId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ;
ALTER TABLE products ADD FOREIGN KEY(productBrand) REFERENCES brands(brandId) ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE items ADD FOREIGN KEY(itemBrand) REFERENCES product(productBrand) ON DELETE RESTRICT ON UPDATE CASCADE;
So here, you have a foreign key constraint, which makes Items inherit the brand specified in Product, which inherits itself from Brands. In this case, if you modify Name for a special BrandId, child tables will have the akncowledge of that. ON DELETE RESTRICT means that you cannot break the chain and database will not allow you to delete an entry that is referenced elsewhere :)
I'm currently breaking my head on a 1452 error (using Symfony2 with doctrine).
Here is the case:
I have 3 tables, Location, Concert and CD, and as expected a Concert take place at a Location, and a CD is related to a Concert, that's including some foreign keys.
Here is the SQL queries generated by doctrine to create the database:
CREATE TABLE CD (id INT AUTO_INCREMENT NOT NULL, concert INT NOT NULL, number INT NOT NULL, INDEX IDX_EB3C8BB0D57C02D2 (concert), PRIMARY KEY(id)) ENGINE = InnoDB;
CREATE TABLE Concert (id INT AUTO_INCREMENT NOT NULL, location INT NOT NULL, date DATETIME NOT NULL, name VARCHAR(255) DEFAULT NULL, INDEX IDX_1AC13B4E5E9E89CB (location), PRIMARY KEY(id)) ENGINE = InnoDB;
CREATE TABLE Location (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(40) NOT NULL, city VARCHAR(40) NOT NULL, PRIMARY KEY(id)) ENGINE = InnoDB;
ALTER TABLE CD ADD CONSTRAINT FK_EB3C8BB0D57C02D2 FOREIGN KEY (concert) REFERENCES Concert(id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE Concert ADD CONSTRAINT FK_1AC13B4E5E9E89CB FOREIGN KEY (location) REFERENCES Location(id) ON UPDATE CASCADE ON DELETE CASCADE;
The fact is, there is no problem to insert Location, and a connected Concert, to satisfy the CD's foreign key:
INSERT INTO Location (id, name, city) VALUES (NULL, 'Church', 'Berlin');
INSERT INTO Concert (id, location, date, name) VALUES (NULL, '1', '2012-06-20 19:30:00', NULL);
But then if I try to insert a CD:
INSERT INTO CD (id ,concert ,number) VALUES (NULL , '1', '1');
I got the famous error:
Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`festival`.`cd`, CONSTRAINT `FK_EB3C8BB0D57C02D2` FOREIGN KEY (`concert`) REFERENCES `Concert` (`id`) ON DELETE CASCADE ON UPDATE CASCADE)
So I've tried some hacks:
Escape the creation of the table Location and the related key:
CREATE TABLE CD (id INT AUTO_INCREMENT NOT NULL, concert INT NOT NULL, number INT NOT NULL, INDEX IDX_EB3C8BB0D57C02D2 (concert), PRIMARY KEY(id)) ENGINE = InnoDB;
CREATE TABLE Concert (id INT AUTO_INCREMENT NOT NULL, location INT NOT NULL, date DATETIME NOT NULL, name VARCHAR(255) DEFAULT NULL, INDEX IDX_1AC13B4E5E9E89CB (location), PRIMARY KEY(id)) ENGINE = InnoDB;
ALTER TABLE CD ADD CONSTRAINT FK_EB3C8BB0D57C02D2 FOREIGN KEY (concert) REFERENCES Concert(id) ON UPDATE CASCADE ON DELETE CASCADE;
That's working well.
Delete the table Location after creation (and the related key):
CREATE TABLE CD (id INT AUTO_INCREMENT NOT NULL, concert INT NOT NULL, number INT NOT NULL, INDEX IDX_EB3C8BB0D57C02D2 (concert), PRIMARY KEY(id)) ENGINE = InnoDB;
CREATE TABLE Concert (id INT AUTO_INCREMENT NOT NULL, location INT NOT NULL, date DATETIME NOT NULL, name VARCHAR(255) DEFAULT NULL, INDEX IDX_1AC13B4E5E9E89CB (location), PRIMARY KEY(id)) ENGINE = InnoDB;
CREATE TABLE Location (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(40) NOT NULL, city VARCHAR(40) NOT NULL, PRIMARY KEY(id)) ENGINE = InnoDB;
ALTER TABLE CD ADD CONSTRAINT FK_EB3C8BB0D57C02D2 FOREIGN KEY (concert) REFERENCES Concert(id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE Concert ADD CONSTRAINT FK_1AC13B4E5E9E89CB FOREIGN KEY (location) REFERENCES Location(id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE Concert DROP FOREIGN KEY FK_1AC13B4E5E9E89CB;
ALTER TABLE Concert DROP INDEX IDX_1AC13B4E5E9E89CB;
DROP TABLE Location;
Same error.
Escape foreign key controlling
SET FOREIGN_KEY_CHECKS = 0;
Same error.
The two last tries, make me think of some index issue on the link Concert-Location ?
You understand, I am a bit lost.
Does anyone have already faced something similar ?
Thank a lot for knowledge,
Tom.
So,
According to MySQL documentation, MySQL server since 5.5.9 on OSX, fails on insertion with foreign keys. This bug seems to come with case sensitivity.
There is many ways to fix it (this list is non-exhaustive, and some options may not be effective):
Use only lowercase tables names.
Try to set lower_case_table_names to O or 1 (but on OS X it's not always possible cause of system's case sensitivity).
Downgrade to MySQL server 5.5.8 (I've not tested upgrading).
Thanks to this thread.
This was originally going to be an "update" on the logical schema presented in another question here: Getting ERROR 1701, ERROR 1452 and ERROR 1305 errors in MySQL - Need some expertise...
I think I have successfully verified this schema to 1st and 2nd Normal Form, but am unsure if this meets 3rd Normal Form. Here is the model in question:
And here is the associated code (note: for some reason I cannot recreate 1:1 relationships in the sql code as illustrated in the logical model above):
-- database_schema.sql.
-- This sql script creates the structure.
-- of the rugby club database.
DROP DATABASE IF EXISTS database_rugby;
CREATE DATABASE database_rugby;
USE database_rugby;
-- Create the "person" table.
--
-- This table has one:one relationships
-- with the parent, coach and player
-- tables.
DROP TABLE IF EXISTS `person` ;
CREATE TABLE `person` (
`personID` INT(5) NOT NULL AUTO_INCREMENT ,
`firstName` VARCHAR(50) NOT NULL ,
`lastName` VARCHAR(50) NOT NULL ,
`dateOfBirth` DATE NOT NULL ,
`streetAddress` VARCHAR(150) NOT NULL ,
`suburbAddress` VARCHAR(150) NULL DEFAULT NULL ,
`cityAddress` VARCHAR(150) NOT NULL ,
`photo` BLOB NULL DEFAULT NULL ,
PRIMARY KEY (`personID`))
ENGINE = InnoDB;
-- Create the "parent" table.
DROP TABLE IF EXISTS `parent` ;
CREATE TABLE `parent` (
`parentID` INT(5) NOT NULL ,
`personID` INT(5) NOT NULL ,
PRIMARY KEY (`parentID`, `personID`),
FOREIGN KEY (`personID`) REFERENCES `person` (`personID`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB;
-- Create the "school" table.
DROP TABLE IF EXISTS `school` ;
CREATE TABLE `school` (
`schoolID` INT(5) NOT NULL AUTO_INCREMENT ,
`schoolName` VARCHAR(100) NOT NULL ,
PRIMARY KEY (`schoolID`))
ENGINE = InnoDB;
-- Create the "player" table.
--
-- Inherits fields from the "person"
-- and "school" tables.
DROP TABLE IF EXISTS `player` ;
CREATE TABLE `player` (
`playerID` INT(5) NOT NULL ,
`personID` INT(5) NOT NULL ,
`schoolID` INT(5) NOT NULL ,
PRIMARY KEY (`playerID`, `personID`),
FOREIGN KEY (`personID`)
REFERENCES `person` (`personID`)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (`schoolID`)
REFERENCES `school` (`schoolID`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB;
-- Create the "coach" table.
DROP TABLE IF EXISTS `coach`;
CREATE TABLE `coach`(
`coachID` INT(5) NOT NULL ,
`dateBeganCoaching` DATE NOT NULL ,
`personID` INT(5) NOT NULL ,
PRIMARY KEY (`coachID`, `personID`),
FOREIGN KEY (`personID`)
REFERENCES `person` (`personID`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB;
-- Create the "family" table.
--
-- This is a linking table
-- that describes the many:many
-- relationship between "parent"
-- and "player" tables.
DROP TABLE IF EXISTS `family` ;
CREATE TABLE `family` (
`parentID` INT(5) NOT NULL ,
`playerID` INT(5) NOT NULL ,
FOREIGN KEY (`playerID` )
REFERENCES `player` (`playerID`)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (`parentID`)
REFERENCES `parent` (`parentID`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- Create the "grade" table.
DROP TABLE IF EXISTS `grade`;
CREATE TABLE `grade`(
`gradeID` INT(5) NOT NULL AUTO_INCREMENT ,
`gradeName` VARCHAR(50) NOT NULL ,
`minWeight` INT(3) NOT NULL ,
`maxWeight` INT(3) NOT NULL ,
`minAge` INT(3) NOT NULL ,
`maxAge` INT(3) NOT NULL ,
`ballSize` INT(1) NOT NULL ,
PRIMARY KEY (`gradeID`) )
ENGINE = InnoDB;
-- Create the "coachQualification" table.
DROP TABLE IF EXISTS `coachQualification` ;
CREATE TABLE `coachQualification` (
`qualID` INT(5) NOT NULL AUTO_INCREMENT ,
`qualName` CHAR(5) NOT NULL ,
`gradeID` INT(5) NOT NULL ,
PRIMARY KEY (`qualID`) ,
FOREIGN KEY (`gradeID`)
REFERENCES `grade` (`gradeID`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB;
-- Create the "homePhone" table.
DROP TABLE IF EXISTS `homePhone` ;
CREATE TABLE `homePhone` (
`homePhoneID` INT(5) NOT NULL AUTO_INCREMENT ,
`homeNumber` CHAR(9) NOT NULL ,
PRIMARY KEY (`homePhoneID`))
ENGINE = InnoDB;
-- Create the "mobilePhone" table.
DROP TABLE IF EXISTS `mobilePhone` ;
CREATE TABLE `mobilePhone` (
`mobilePhoneID` INT(5) NOT NULL AUTO_INCREMENT ,
`mobileNumber` CHAR(10) NULL DEFAULT NULL ,
PRIMARY KEY (`mobilePhoneID`))
ENGINE = InnoDB;
-- Create the "emailAddress" table.
DROP TABLE IF EXISTS `emailAddress` ;
CREATE TABLE `emailAddress` (
`emailAddressID` INT(5) NOT NULL AUTO_INCREMENT ,
`emailAddress` CHAR(10) NULL DEFAULT NULL ,
PRIMARY KEY (`emailAddressID`))
ENGINE = InnoDB;
-- Create the "Contact" table
--
-- This is a linking table
-- that describes the many:many
-- relationships between "person"
-- and the "homePhone", "mobilePhone",
-- and "emailAddress" tables.
DROP TABLE IF EXISTS `contact` ;
CREATE TABLE `contact` (
`personID` INT(5) NOT NULL ,
`homePhoneID` INT(5) NOT NULL ,
`mobilePhoneID` INT(5) NULL DEFAULT NULL ,
`emailAddressID` INT(5) NULL DEFAULT NULL ,
FOREIGN KEY (`personID` )
REFERENCES `person` (`personID`)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (`homePhoneID`)
REFERENCES `homePhone` (`homePhoneID`)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (`mobilePhoneID`)
REFERENCES `mobilePhone` (`mobilePhoneID`)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (`emailAddressID`)
REFERENCES `emailAddress` (`emailAddressID`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB;
-- Create the "qualificationSet" table.
--
-- This is a linking table
-- that describes the many:many
-- relationship between "coach"
-- and "coachQualification" tables.
DROP TABLE IF EXISTS `qualificationSet` ;
CREATE TABLE `qualificationSet` (
`coachID` INT(5) NOT NULL ,
`qualID` INT(5) NOT NULL ,
FOREIGN KEY (`coachID`)
REFERENCES `coach` (`coachID`)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (`qualID`)
REFERENCES `coachQualification` (`qualID`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB;
-- Create the "team" table.
DROP TABLE IF EXISTS `team` ;
CREATE TABLE `team` (
`teamID` INT(5) NOT NULL AUTO_INCREMENT ,
`teamName` VARCHAR(50) NOT NULL ,
`teamYear` INT(2) NOT NULL ,
`gradeID` INT(5) NOT NULL ,
PRIMARY KEY (`teamID`) ,
FOREIGN KEY (`gradeID`)
REFERENCES `grade` (`gradeID`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB;
-- Create the "teamAllocation" table
--
-- this is a linking table for a
-- many:many relationship between
-- team and player tables.
DROP TABLE IF EXISTS `teamAllocation` ;
CREATE TABLE `teamAllocation` (
`teamID` INT(5) NOT NULL ,
`playerID` INT(5) NOT NULL ,
FOREIGN KEY (`teamID` )
REFERENCES `team` (`teamID`)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (`playerID`)
REFERENCES `player` (`playerID`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB;
-- Create the "teamCoachAllocation" table.
--
-- This is a linking table
-- that describes the many:many
-- relationship between "coach"
-- and "team" tables.
DROP TABLE IF EXISTS `teamCoachAllocation` ;
CREATE TABLE `teamCoachAllocation` (
`coachID` INT(5) NOT NULL ,
`teamID` INT(5) NOT NULL ,
FOREIGN KEY (`coachID`)
REFERENCES `coach` (`coachID`)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (`teamID`)
REFERENCES `team` (`teamID`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB;
From these links below:
http://en.wikipedia.org/wiki/First_normal_form
http://en.wikipedia.org/wiki/Second_normal_form
http://en.wikipedia.org/wiki/Third_normal_form
This is my understanding of Normalisation to 3NF:
First normal form means to not allow repeating values
Second normal form means 1NF and attributes are dependant on whole primary key and not part of a primary key (I think of this as partitioning tables if values in that table need to relate to eachother in some way and have comparisons made).
Third normal form means 2NF and no transistive values (e.g if x = y and y = z, x = z)
Putting that knowledge from theory into practice is quite hard for me, especially translating that "practise" into working, normalised MySQL code. If someone is able to help me go through the model and give me some pointers about normalising the model to 3NF, I would appreciate it very much.
Thanks in advance!
I think this isn't in 3NF, around the contact table. If I'm wrong this is still a bad way of storing the data and should probably be changed.
Sorry if this is a little confused...
It is entirely possible to have the following structure in your contact table as the entire table is the primary key:
+----------+-------------+---------------+---------+
| personid | homephoneid | mobilephoneid | emailid |
+----------+-------------+---------------+---------+
| 1 | 1 | 1 | 1 |
| 1 | 1 | 1 | 2 |
| 1 | 1 | 2 | 3 |
+----------+-------------+---------------+---------+
As you can see both homephoneid and mobilephoneid are duplicated so updating the table homephone will result 3 updates to contact.
I disagree with the data-model as you require a person to have a homehone I don't have one, only a mobile. In this situation, when creating a new person you have to also create a new contact and a new homephone.
As contact is just a primary key and a primary key value cannot be null, you also require the creation of a mobilephone and an emailaddress, which means that person is dependent on emailaddress.
As emailaddress is dependent on contact, which in turn is dependent on person you've created a circular dependency, which breaks 3NF.
As I see it you have a two options if you want to ensure that people have to have a home phone number:
If you only want a person to have one homephone then add this
into the person table. It's unique contact level information and
should be stored there.
If you want to enable people to have multiple home phone numbers -
remembering that multiple people can use the same phone number - but
don't care about mobiles then you need to create a table
personhomephones, say, with the primary key personid,
homephoneid and not put homephoneid in the contact table.
Personally I wouldn't do either of these. I wouldn't ensure that someone has to have a home phone number but instead a primary phone number, where you don't care what type it is. I would allow people to add different methods of contact but allow these not to exist
This would require the following structure:
person - add primaryPhoneID
primaryphone ( primaryphoneID, phonenumber) - PK primaryphoneID
Then for the contact methods that are allowed to not exist:
contactType ( contactTypeID, contactType ) - PK contactTypeID
contact ( contactID, contactTypeID, value ) - PK contactID, contactTypeID
personContact ( personID, contactID, contactTypeID ) - PK everything
Whilst this may result in duplication between contact and primaryphone they are distinct bits of data and I think this is fine. If you're insistent on not allowing any duplication at all you'd have to separate out the phones from the other methods of contact, which makes the model more complicated:
phonetype ( phoneTypeId, phoneType )
phone ( phoneID, phoneTypeID, phonenumber) - PK phoneID, phoneTypeID
contactPhone ( personID, phoneTypeID, phoneID ) - PK everything
There is an algorithm to eventually decompose each relation in your schema in order to get an equivalent schema in 3NF. Google is good for that!!
To get tips about your schema design you should at least describe the context and the functional constraints about the entities you need to represent.