MySQL structure for personalized users access per site? - mysql

I need some help to structure some tables that will help me out having a good personalized way to have different access name per client with personalized allowed commands in each acess level.
So consider client A has create site Y, which he wants to create the follow access groups:
Admin (have all acess),
VIP (can access room 1),
Basic (can access louge),
Banned (cannot enter at all)
The same way client A create his personalized access I want all my other clients to be able to create their own personalized access where they can name it anything they see fit in their own language and later just put in what commands they will have access to by being on that given access level.
I have the follow tables as example (sorry that this isnt a datagram, work bench keep crashing on me when I make example and I am open to suggestion for alternatives to create datagrams as good as or better than workbench):
client TABLE:
id int,
username varchar,
password varchar,
status varchar
site TABLE:
id int,
owner_client_id,
name varchar
access TABLE:
id int,
name varchar,
commands int (bitmask?)
site_access TABLE
id int,
client_id,
site_id,
access_id
commands
id int,
action varchar,
alias varchar,
description varchar
On my application all the commands actions are alredy pre-defined and the user cannot change them in what they do or the default name, but they are allowed to create alias.
So let's say I have the command kick, they could make an alias to name it "k" or "explosion" or they could name the alias anything they want.
Some of my doubts:
Initially I tought of using site_access to link everything together, client that has access to site and what access it has and from their access what commands each have, is that a good way to go with it ?
I have many commands that will be pulled from the database but since some have their own alias I dont thin I could use a bitmask for the acess and still being able to query the alias if not null could I so I would have to use a list of commands or are there good options ?
What engine should I use, InnoDB or MyISAM in my case ?
Is it ok to mix engines or not a good idea at all ?
What should I change on my current table structure and could you provide any samples too (if possible) ?
UPDATE:
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';
CREATE SCHEMA IF NOT EXISTS `mydb` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci ;
USE `mydb` ;
-- -----------------------------------------------------
-- Table `mydb`.`clients`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `mydb`.`clients` ;
CREATE TABLE IF NOT EXISTS `mydb`.`clients` (
`id` INT NOT NULL,
`username` VARCHAR(45) NOT NULL ,
`password` VARCHAR(45) NOT NULL ,
PRIMARY KEY (`id`) ,
UNIQUE INDEX `username_UNIQUE` (`username` ASC) )
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`sites`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `mydb`.`sites` ;
CREATE TABLE IF NOT EXISTS `mydb`.`sites` (
`id` INT NOT NULL AUTO_INCREMENT ,
`name` VARCHAR(45) NOT NULL ,
PRIMARY KEY (`id`) )
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`groups`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `mydb`.`groups` ;
CREATE TABLE IF NOT EXISTS `mydb`.`groups` (
`id` INT NOT NULL ,
`name` VARCHAR(45) NOT NULL ,
`alias` VARCHAR(45) NOT NULL ,
`commands` VARCHAR(45) NOT NULL ,
PRIMARY KEY (`id`) )
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`membership`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `mydb`.`membership` ;
CREATE TABLE IF NOT EXISTS `mydb`.`membership` (
`client_id` INT NOT NULL ,
`group_id` INT NOT NULL ,
PRIMARY KEY (`client_id`, `group_id`) ,
INDEX `client_id` (`client_id` ASC) ,
INDEX `group_id` (`group_id` ASC) ,
CONSTRAINT `client_id`
FOREIGN KEY (`client_id` )
REFERENCES `mydb`.`clients` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `group_id`
FOREIGN KEY (`group_id` )
REFERENCES `mydb`.`groups` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`access`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `mydb`.`access` ;
CREATE TABLE IF NOT EXISTS `mydb`.`access` (
`site_id` INT NOT NULL ,
`group_id` INT NOT NULL ,
PRIMARY KEY (`site_id`, `group_id`) ,
INDEX `site_id` (`site_id` ASC) ,
INDEX `group_id` (`group_id` ASC) ,
CONSTRAINT `site_id`
FOREIGN KEY (`site_id` )
REFERENCES `mydb`.`sites` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `group_id`
FOREIGN KEY (`group_id` )
REFERENCES `mydb`.`groups` (`id` )
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;

I would do it a bit differently
Client Table
Group Table
Membership Table (Client - Group)
Site Table
Access Table (Group Site)
Also see pages 12-13 in this link for ideas http://support.sas.com/resources/papers/proceedings09/265-2009.pdf

Related

Enforcing single subtype SQL table for supertype table

I have a supertype table called "vehicles". I also have three subtype tables called "airplanes", "automobiles", and "bicycles", and one and only one of these subtype tables must be linked to the vehicles supertype table (or in other words, must use the vehicles primary key ID as its primary key ID).
How should this be modeled to enforce this behavior?
EDIT Proposed schema recommended by Mike Brant.
-- MySQL Script generated by MySQL Workbench
-- 05/25/16 09:20:17
-- 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`.`vehicle_types`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`vehicle_types` (
`type` CHAR(8) NOT NULL,
PRIMARY KEY (`type`))
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`vehicles`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`vehicles` (
`idvehicles` INT NOT NULL AUTO_INCREMENT,
`type` CHAR(8) NOT NULL,
`data` VARCHAR(45) NULL,
PRIMARY KEY (`idvehicles`),
INDEX `fk_vehicles_vehicle_types_idx` (`type` ASC),
CONSTRAINT `fk_vehicles_vehicle_types`
FOREIGN KEY (`type`)
REFERENCES `mydb`.`vehicle_types` (`type`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`airplanes`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`airplanes` (
`vehicles_idvehicles` INT NOT NULL,
`data_for_airplanes` VARCHAR(45) NULL,
PRIMARY KEY (`vehicles_idvehicles`),
CONSTRAINT `fk_airplanes_vehicles1`
FOREIGN KEY (`vehicles_idvehicles`)
REFERENCES `mydb`.`vehicles` (`idvehicles`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`automobiles`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`automobiles` (
`vehicles_idvehicles` INT NOT NULL,
`data_for_automobiles` VARCHAR(45) NULL,
PRIMARY KEY (`vehicles_idvehicles`),
CONSTRAINT `fk_automobiles_vehicles1`
FOREIGN KEY (`vehicles_idvehicles`)
REFERENCES `mydb`.`vehicles` (`idvehicles`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`bicycles`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`bicycles` (
`vehicles_idvehicles` INT NOT NULL,
`data_for_bicycles` VARCHAR(45) NULL,
PRIMARY KEY (`vehicles_idvehicles`),
CONSTRAINT `fk_bicycles_vehicles1`
FOREIGN KEY (`vehicles_idvehicles`)
REFERENCES `mydb`.`vehicles` (`idvehicles`)
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;
I think inserting in vehicles and any of other sub types should be simultaneously. Therefore you can get the vehicle ID and put as new sub-type PK ID. If you have all information at the same time you can create a transcation and insert the vehicle record first then the sub-type record. Then commit your transscation otherwise rollback it. IF you want to know which record belongs to which sub-type table record you can add a column to your vehicle table that for example can have 3 different values such as 0,1,2 that shows which record belongs to which sub-table. Enum in java can be used in something like that for more clarification
Looking at your proposed schema, I see no reason whatsoever for you to have separate tables for each vehicle type, or even a separate table to contain the allowable vehicle types. Your vehicle-specific tables are all basically the same thing meaning you can easily collapse into a single table, and you can use ENUM field to enforce allowable vehicle types.
Why not just have a single vehicles table like the following?
CREATE TABLE IF NOT EXISTS `mydb`.`vehicles` (
`idvehicles` INT NOT NULL AUTO_INCREMENT,
`type` ENUM('airplane', 'automobile', 'bicycle') NOT NULL,
`data` VARCHAR(45) NULL,
PRIMARY KEY (`idvehicles`),
INDEX `fk_vehicles_vehicle_types_idx` (`type` ASC))
ENGINE = InnoDB;
This approach totally eliminates 4 of your 5 tables, meaning you no longer have to consider using joins, foreign key constraints, etc. when performing CRUD operations against these records.
To enforce exclusive subtypes, copy the type indicator into each of the subtype tables and use composite foreign key constraints:
CREATE TABLE IF NOT EXISTS `mydb`.`airplanes` (
`vehicles_idvehicles` INT NOT NULL,
`type` CHAR(8) NOT NULL,
`data_for_airplanes` VARCHAR(45) NULL,
PRIMARY KEY (`vehicles_idvehicles`),
CONSTRAINT `fk_airplanes_vehicles1`
FOREIGN KEY (`vehicles_idvehicles`, `type`)
REFERENCES `mydb`.`vehicles` (`idvehicles`, `type`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
Next you need to restrict the value of type in each table. Unfortunately, MySQL doesn't support check constraints, so you would need to use triggers:
DELIMITER ;;
CREATE TRIGGER airplanes_insert_type_check
BEFORE INSERT ON airplanes
FOR EACH ROW
BEGIN
IF NEW.`type` != 'airplane' THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Invalid type in airplanes';
END IF;
END;;
CREATE TRIGGER airplanes_update_type_check
BEFORE UPDATE ON airplanes
FOR EACH ROW
BEGIN
IF NEW.`type` != 'airplane' THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Invalid type in airplanes';
END IF;
END;;
DELIMITER ;
Thus, the type indicator in the supertype table will match only one of the subtype tables' type indicators due to the trigger restrictions, and will be enforced via foreign key constraint, preventing overlapping subtypes.

Transferring MySQL Workbench to Server

I am trying to move my database model from mysql workbench to mysql server. I am using the reverse engineer sql create script but when importing it gives me an error. I have tried to google the problem but no luck to my situation.
-- -----------------------------------------------------
-- Table `MapLibrary`.`Books`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `MapLibrary`.`Books` ;
CREATE TABLE IF NOT EXISTS `MapLibrary`.`Books` (
`ISBN` VARCHAR(13) NOT NULL,
`date_of_publication` INT NULL,
`book_title` VARCHAR(45) NULL,
PRIMARY KEY (`ISBN`))
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `MapLibrary`.`Genre`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `MapLibrary`.`Genre` ;
CREATE TABLE IF NOT EXISTS `MapLibrary`.`Genre` (
`genre_code` INT NOT NULL,
`genre_name` VARCHAR(45) NOT NULL,
PRIMARY KEY (`genre_code`))
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `MapLibrary`.`Books_By_Genre`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `MapLibrary`.`Books_By_Genre` ;
CREATE TABLE IF NOT EXISTS `MapLibrary`.`Books_By_Genre` (
`genre_code` INT NOT NULL,
`ISBN` VARCHAR(13) NOT NULL,
PRIMARY KEY (`genre_code`, `ISBN`),
INDEX `Books_idx` (`ISBN` ASC),
CONSTRAINT `Books`
FOREIGN KEY (`ISBN`)
REFERENCES `MapLibrary`.`Books` (`ISBN`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `Genre`
FOREIGN KEY (`genre_code`)
REFERENCES `MapLibrary`.`Genre` (`genre_code`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
ERROR:
ERROR 1022 (23000) at line 89: Can't write; duplicate key in table 'books_by_genre'
Operation failed with exitcode 1
The problem is that the ISBN is NOT a unique identifier on its own in the referenced tables.
I assume, that the Books table is your base table and the three referenced tables are M:N link tables. If this is true, then your foreign keys are pointing in the wrong direction. (Try to drag the connection in the other direction?)

Referential integrity for invoice and task tables

I am having trouble figuring out how to design a database for an invoicing system. I want to have an invoice table and an tasks table. I want a record in the invoices table to be able to have a variable amount of tasks associated with it but also keep referential integrity so that a task cannot be added that does not exist on the tasks table.
So far I have come up with a task_id1, task_id2, etc... fields on the invoices table that are foreign keys to the task_id field in the tasks table but it feels limiting in the number of tasks that can be put on one invoice.
Alternatively the invoices table could have a comma separated list of task_id's that would allow for a variable amount of tasks per invoice but I cant figure out how to create the foreign key if the column types are not the same. (Plus this feels a little sloppy).
Im sure there is an easy answer I am not seeing.
So far I have come up with a task_id1, task_id2, etc... fields on the
invoices table that are foreign keys to the task_id field
Alternatively the invoices table could have a comma separated list of
task_id's that would allow for a variable amount of tasks per invoice
NOO!!!
Make a many-to-many tables between invoices and tasks, and make the constraint between this table and both the invoice and task table.
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';
CREATE SCHEMA IF NOT EXISTS `mydb` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci ;
USE `mydb` ;
-- -----------------------------------------------------
-- Table `mydb`.`invoices`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`invoices` (
`id` INT NOT NULL AUTO_INCREMENT ,
`date` DATETIME NULL ,
`amount` DECIMAL(7,2) NULL ,
`whatever` VARCHAR(45) NULL ,
PRIMARY KEY (`id`) )
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`tasks`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`tasks` (
`id` INT NOT NULL AUTO_INCREMENT ,
`whatever` VARCHAR(45) NULL ,
PRIMARY KEY (`id`) )
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`invoices_has_tasks`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`invoices_has_tasks` (
`invoices_id` INT NOT NULL ,
`tasks_id` INT NOT NULL ,
PRIMARY KEY (`invoices_id`, `tasks_id`) ,
INDEX `fk_invoices_has_tasks_tasks1_idx` (`tasks_id` ASC) ,
INDEX `fk_invoices_has_tasks_invoices_idx` (`invoices_id` ASC) ,
CONSTRAINT `fk_invoices_has_tasks_invoices`
FOREIGN KEY (`invoices_id` )
REFERENCES `mydb`.`invoices` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_invoices_has_tasks_tasks1`
FOREIGN KEY (`tasks_id` )
REFERENCES `mydb`.`tasks` (`id` )
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;
To create the invoice table
CREATE TABLE invoice (
invoice_id INT NOT NULL,
PRIMARY KEY (invoice_id)
) ENGINE=INNODB;
To create the tasks table use
CREATE TABLE tasks (
task_id INT,
invoice_id INT,
INDEX par_ind (invoice_id),
FOREIGN KEY (invoice_id)
REFERENCES invoice(invoice_id)
ON DELETE CASCADE
) ENGINE=INNODB;
That is all you need

Need some assistance in verifying a database logical schema to Third Normal Form

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.

MySQL Triggers - Implementation Issue

I am developing a MySQL database using Workbench. I want two send two fields from a newly created record to another table. I would then like to update the original table with newly created data from the second table. I was looking to implement this with triggers, unless there is a better way of course :) My attempt was a fail when I went to upload it(see below)
Specifically, I would like tc_Event to send the ID & tc_EventTags_ID to tc_EventTags to fill in tc_Tag_ID & tc_Event_ID. Afterwards I want the ID of tc_EventTags sent back to tc_Event to the tc_EventTags_ID field.
Thanks for any help.
-- -----------------------------------------------------
-- Table `mcontest`.`tc_EventTags`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mcontest`.`tc_EventTags` (
`ID` INT NOT NULL AUTO_INCREMENT ,
`tc_Tag_ID` INT NOT NULL ,
`tc_Event_ID` INT NOT NULL ,
PRIMARY KEY (`ID`) ,
INDEX `fk_tc_EventTags_tc_Tag1` (`tc_Tag_ID` ASC) ,
INDEX `fk_tc_EventTags_tc_Event1` (`tc_Event_ID` ASC) ,
CONSTRAINT `fk_tc_EventTags_tc_Tag1`
FOREIGN KEY (`tc_Tag_ID` )
REFERENCES `mcontest`.`tc_Tag` (`ID` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_tc_EventTags_tc_Event1`
FOREIGN KEY (`tc_Event_ID` )
REFERENCES `mcontest`.`tc_Event` (`ID` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = MyISAM;
-- -----------------------------------------------------
-- Table `mcontest`.`tc_Event`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mcontest`.`tc_Event` (
`ID` INT NOT NULL AUTO_INCREMENT ,
`date` DATE NOT NULL ,
`time` TIME NOT NULL ,
`location` VARCHAR(45) NOT NULL ,
`description` VARCHAR(45) NOT NULL ,
`tc_EventTags_ID` INT NULL ,
`tc_Orgs_ID` INT NOT NULL ,
`tc_PersonEvent_ID` INT NOT NULL ,
PRIMARY KEY (`ID`) ,
INDEX `fk_tc_Event_tc_EventTags1` (`tc_EventTags_ID` ASC) ,
INDEX `fk_tc_Event_tc_Orgs1` (`tc_Orgs_ID` ASC) ,
INDEX `fk_tc_Event_tc_PersonEvent1` (`tc_PersonEvent_ID` ASC) ,
CONSTRAINT `fk_tc_Event_tc_EventTags1`
FOREIGN KEY (`tc_EventTags_ID` )
REFERENCES `mcontest`.`tc_EventTags` (`ID` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_tc_Event_tc_Orgs1`
FOREIGN KEY (`tc_Orgs_ID` )
REFERENCES `mcontest`.`tc_Orgs` (`ID` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_tc_Event_tc_PersonEvent1`
FOREIGN KEY (`tc_PersonEvent_ID` )
REFERENCES `mcontest`.`tc_PersonEvent` (`ID` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = MyISAM;
USE `mcontest`;
DELIMITER $$
USE `mcontest`$$
CREATE TRIGGER eventTag_Trigger
AFTER insert ON tc_Event
FOR EACH ROW BEGIN
INSERT INTO tc_EventTags values('',NEW.tc_Event_ID);
END;
END$$
DELIMITER ;
SET SQL_MODE=#OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=#OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=#OLD_UNIQUE_CHECKS;
Why have put a tc_EventTags_ID in table tc_Event? What is the logic behind that?
I mean that the relationship between the 2 tables would be (I guess): 1 Event - many EventTags. This is already achieved by the tc_EventTags.tc_Event_ID which is a Foreign Key to tc_Event.
To answer your question:
As it is now, the Trigger tries for every row inserted in table Event, to add a row in table EventTag. But it will fail for 2 reasons:
EventTag has 2 Constraints (Foreign keys) which have to be fulfilled for the triggered insert to succeed. So, tc_Event_ID is ok but tc_Tag_ID has to be NOT NULL and reference the Tag table but the value you supply, '', probably is not in table Tag.
EDIT: the value you supply, '', is also a CHAR while it should be INT.
But trigger will probably not be started at all, since every time you insert a row in table Event, those constraints have to be fulfilled and one of them is the tc_EventTags_ID which references EventTag table. But EventTag table is empty. Do you see the circular logic here?