mysql innodb basic design for joomla component - mysql

I have four entities:
person
center
activity
address
So my idea is:
A person may have an associated address.
A center may have an associated address.
An activity may have an associated address.
Is this mysql design correct for a joomla component?
CREATE TABLE `#__person` (
`id` int(10) NOT NULL AUTO_INCREMENT,
other fields...
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `#__center` (
`id` int(10) NOT NULL AUTO_INCREMENT,
other fields...
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `#__activity` (
`id` int(10) NOT NULL AUTO_INCREMENT,
other fields...
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `#__address` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`line1` varchar(30),
`line2` varchar(30),
`locality` varchar(10),
`region` varchar(10),
`country` varchar(10),
`postcode` varchar(10),
`person_id` int(10),
`center_id` int(10),
`activity_id` int(10),
PRIMARY KEY (id),
FOREIGN KEY `person_id` REFERENCES `#__person` (id) ON DELETE CASCADE,
FOREIGN KEY `center_id` REFERENCES `#__center` (id) ON DELETE CASCADE,
FOREIGN KEY `activity_id` REFERENCES `#__activity` (id) ON DELETE CASCADE,
other fields...
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
So i.e:
If I delete a person, automatically his/her corresponding address will be deleted?
And if I delete an address, what will happen to their references? (hope nothing)
What happens if the same address is an address for a center and for an activity?
Are the foreign keys in the correct table or should i put an address_id field in person,center and activity?
I'm a bit confused about my design.
Thank you for your suggestions.

Instead of placing an id for person, center and activity into the address table, I would suggest placing an address_id into each of the other tables (person, center, and activity). Then, you could reuse the same address across the other tables and in different combinations of person, center, and activity.
And about cascade, I think the MySQL Reference manual explains it well:
CASCADE: Delete or update the row from the parent table, and
automatically delete or update the matching rows in the child table.
Both ON DELETE CASCADE and ON UPDATE CASCADE are supported. Between
two tables, do not define several ON UPDATE CASCADE clauses that act
on the same column in the parent table or in the child table.
So if you did change address_id to be in each of the other tables and then you applied the foreign key constraint
FOREIGN KEY `address_id` REFERENCES `#__address` (id) ON DELETE CASCADE
then when you deleted the row in the address table (the one WITHOUT the foreign key constraint), the matching rows in either person, center, or activity (where the foreign keys are defined), would be deleted. BUT, I am guessing that this might not be what you want, because you said a person, center and activity "may" have an address. So if you deleted the address, you do not necessarily want to delete the person, center or activity that was using that address. If that is true, then I would change your foreign key constraint in each of the 3 tables to:
FOREIGN KEY `address_id` REFERENCES `#__address` (id) ON DELETE SET NULL

Related

error 1215: database design with foreign key

I am trying to create three tables such as associate, manager and attendance. The attendance table should be having employee and manager details from the other two table which should enable marking the attendance. I created this SQL script. I'm not sure where I am making mistake.
CREATE TABLE associate (
id INT NOT NULL,
idmanager INT NOT NULL,
emp_id DATE NOT NULL,
emp_name VARCHAR(25) NOT NULL,
FOREIGN KEY (id) REFERENCES attendance (associate_id) ON DELETE CASCADE,
FOREIGN KEY (idmanager) REFERENCES attendance (manager_idmanager) ON DELETE CASCADE,
PRIMARY KEY (id)
) ENGINE=INNODB;
CREATE TABLE manager (
id INT NOT NULL,
mgr_usr_id VARCHAR(15) NOT NULL,
mgr_name VARCHAR(25) NOT null,
KEY (id),
KEY (mgr_usr_id),
FOREIGN KEY (id) REFERENCES associate (idmanager) ON DELETE CASCADE,
PRIMARY KEY (id)
) ENGINE=INNODB;
CREATE TABLE attendance (
sno INT NOT NULL,
manager_idmanager INT NOT NULL,
associate_id INT NOT NULL,
date_stamp DATETIME,
state BIT NOT NULL,
PRIMARY KEY (sno)
) ENGINE=INNODB;
Screenshot
It's an issue of ordering. For example, the first statement executed is
CREATE TABLE associate (
which references attendance. However, the attendance table has not yet been created. Switch the order so that any tables that reference other tables come last.
Alternatively, don't put the FOREIGN KEY constraints in the CREATE statements, but them at the end of your script with ALTER TABLE statements. Consider:
CREATE TABLE associate (
id INT NOT NULL,
idmanager INT NOT NULL,
emp_id DATE NOT NULL,
emp_name VARCHAR(25) NOT NULL,
PRIMARY KEY (id)
) ENGINE=INNODB;
CREATE TABLE attendance (
sno INT NOT NULL,
manager_idmanager INT NOT NULL,
associate_id INT NOT NULL,
date_stamp DATETIME,
state BIT NOT NULL,
PRIMARY KEY (sno)
) ENGINE=INNODB;
ALTER TABLE associate ADD FOREIGN KEY (id) REFERENCES associate(id) ON DELETE CASCADE;
Edit
The above is just syntax. To model the requested problem consider orthogonality of information. You might also see/hear "normalization." The basic concept is this: have only one copy of your information. The schema should have a single point of authority for all data. For example, if a user has a birthdate, make sure you don't have an ancillary column that also stores their birthday; it's superfluous information and can lead to data errors.
In this case, what is the relationship? What must come first for the other to exist? Can an attendance be had without a manager? How about a manager without attendance? The former makes no sense. In this case then, I would actually use a third table, to form a hierarchy.
Then, consider that maybe roles change in a company. It would not behoove the DB architect to hard code roles as tables. Consider:
CREATE TABLE employee (
id INTEGER NOT NULL AUTO_INCREMENT,
name VARCHAR(25) NOT NULL,
PRIMARY KEY (id)
) ENGINE=INNODB;
CREATE TABLE role (
id INTEGER NOT NULL AUTO_INCREMENT,
name VARCHAR(30) NOT NULL,
description VARCHAR(254) NOT NULL,
PRIMARY KEY( id ),
UNIQUE( name )
) ENGINE=INNODB;
INSERT INTO role (name, description) VALUES
('associate', 'An associate is a ...'),
('manager', 'A manager follows ...');
CREATE TABLE employee_role (
employee_id INTEGER NOT NULL,
role_id INTEGER NOT NULL,
PRIMARY KEY (employee_id, role_id),
FOREIGN KEY (idemployee_id) REFERENCES employee_id (id) ON DELETE CASCADE,
FOREIGN KEY (role_id) REFERENCES role (id) ON DELETE CASCADE
) ENGINE=INNODB;
CREATE TABLE attendance (
sno INTEGER NOT NULL,
employee_id INTEGER NOT NULL,
date_stamp DATETIME,
state BIT NOT NULL,
PRIMARY KEY (sno),
FOREIGN KEY (idemployee_id) REFERENCES employee_id (id) ON DELETE CASCADE
) ENGINE=INNODB;
From this schema, the attendance needs only one foreign key because everyone is an employee. Employee's can have multiple roles, and they can change. Further, role definitions can change without needing to resort to costly DDL statements (data definition layer changes, like ALTER TABLE), and can be modified with simple DML (data manipulation layer changes, like UPDATE TABLE). The former involves rewriting all entries in the tables, and changing schemas, while the latter involves changing individual entries.

How can I delete the rows of a table which stores foreign keys?

THE SOLUTION IS BELOW
I have three tables like the following:
CREATE TABLE `t_arch_layer` (
`arch_layer_id` int(11) NOT NULL AUTO_INCREMENT,
`arch_layer_name` varchar(45) NOT NULL,
PRIMARY KEY (`arch_layer_id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
CREATE TABLE `t_tech` (
`tech_id` int(11) NOT NULL AUTO_INCREMENT,
`tech_name` varchar(45) DEFAULT NULL,
`tech_type_id` int(11) NOT NULL,
`tech_icon` text,
PRIMARY KEY (`tech_id`),
KEY `fk_t_tech_t_tech_type1_idx` (`tech_type_id`),
CONSTRAINT `fk_t_tech_t_tech_type1` FOREIGN KEY (`tech_type_id`) REFERENCES `t_tech_type` (`tech_type_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8;
CREATE TABLE `t_arch_layer_tech` (
`arch_layer_id` int(11) NOT NULL,
`tech_id` int(11) NOT NULL,
PRIMARY KEY (`tech_id`,`arch_layer_id`),
KEY `fk_t_layer_has_t_tech_t_tech1_idx` (`tech_id`),
KEY `fk_t_layer_has_t_tech_t_layer1_idx` (`arch_layer_id`),
CONSTRAINT `fk_t_layer_has_t_tech_t_layer1` FOREIGN KEY (`arch_layer_id`) REFERENCES `t_arch_layer` (`arch_layer_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_t_layer_has_t_tech_t_tech1` FOREIGN KEY (`tech_id`) REFERENCES `t_tech` (`tech_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Basically it's a tipical situation where one table use two foreign keys from another two different tables. This table stores the possible combinations between the layers and technologies so it can't store any combination of layer_id and tech_id which is not in both.
But there is a problem, I need to delete whenever I want some row from t_arch_layer_tech. This it's impossible due to the foreign keys, I know it.
My question is, is there something to use the foreign key as a reference to forbide insert values that there aren't into t_tech or t_arch_layer and also to be consider as "own fields" (I can't explain better) of the table in order to delete any row of the t_arch_layer_tech table? Delete t_tech and t_arch_layer tables to avoid the foreign keys and then set the limits into the t_arch_layer_tech is not a solution.
SOLUTION
When that error appears it's neccesary to check the DB relationships and read carefully the provided message. It seems useless but it helped me to understand what's happening with the t_arch_layer_tech FK. I was using them into another table BUT separately, not as a compound FK. This is the reason because I could insert some rows into t_arch_layer_tech and delete only specific pairs.
So, summarizing, if you are going to use FKs that exist together (as my pair "arch_layer_id, tech_id") create ONLY ONE FK which is a compound FK that uses the mentioned.

MySql - Product Variants Table (Wide Table) - Unique with NULLs

I have a products table, and a product_variants table (one-to-many).
The product_variants table has the following structure:
CREATE TABLE product_variants (
id int(11) NOT NULL AUTO_INCREMENT,
id_product int(11) NOT NULL,
id_colourSet int(11) DEFAULT NULL,
id_size int(11) DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY UNIQUE (id_product,id_colourSet,id_size),
KEY idx_prod (id_product),
KEY idx_colourSet (id_colourSet),
KEY idx_size (id_size),
CONSTRAINT fk_df_product_variants_id_colurSet FOREIGN KEY (id_colourSet) REFERENCES df_colour_sets (id_colourSet) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_df_product_variants_id_product FOREIGN KEY (id_product) REFERENCES df_products (id) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_df_product_variants_id_size FOREIGN KEY (id_size) REFERENCES df_sizes (id) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB
The options are known at compile-time. Each option is foreign-keyed to a dedicated table, and the unique key is the combination of all options.
I then insert products with an "ON DUPLICATE KEY UPDATE ..." statement, and if a variant already exists the query will use an existing variant.
The problem is that certain products do not have a color, nor a size. In this case the unique constraint fails and I insert lots of almost-empty rows in the product_variants table.
In order to solve this problem I am creating a "NULL" value for each option (e.g. "NO_COLOR", "NO_SIZE") in the respective option tables, and using that as the default value for the option columns in the product_variants table.
Would this be the recommended solution? Is there a better way of structuring this data? I would really like to avoid an EAV design.
Thank you
Designating a magic value that means "missing value" is not the right solution in almost every case. That's what NULL is for.
It's also not clear how "NO_COLOR" is used for an integer. I guess it would map to the value 0, which is typically not used in an auto-increment column.
You can create another column to be a hash of the three unique key columns, defaulted to '' to avoid null problems. Then put a unique constraint on that hash.
CREATE TABLE product_variants (
id int(11) NOT NULL AUTO_INCREMENT,
id_product int(11) NOT NULL,
id_colourSet int(11) DEFAULT NULL,
id_size int(11) DEFAULT NULL,
option_hash binary(16) NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY (option_hash),
KEY idx_prod (id_product),
KEY idx_colourSet (id_colourSet),
KEY idx_size (id_size),
CONSTRAINT fk_df_product_variants_id_colurSet FOREIGN KEY (id_colourSet) REFERENCES df_colour_sets (id_colourSet) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_df_product_variants_id_product FOREIGN KEY (id_product) REFERENCES df_products (id) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_df_product_variants_id_size FOREIGN KEY (id_size) REFERENCES df_sizes (id) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
CREATE TRIGGER product_variants_ins BEFORE INSERT ON product_variants
FOR EACH ROW SET option_hash = UNHEX(MD5(CONCAT_WS('|',
COALESCE(id_product, ''),
COALESCE(id_colourSet, ''),
COALESCE(id_size, ''))));
CREATE TRIGGER product_variants_upd BEFORE UPDATE ON product_variants
FOR EACH ROW SET option_hash = UNHEX(MD5(CONCAT_WS('|',
COALESCE(id_product, ''),
COALESCE(id_colourSet, ''),
COALESCE(id_size, ''))));

Recursive table

I'm trying to create a table with a recursive column, but It doesn't work. Later I tried it and it did work, but the I dropped the table because of a problem and now I don't know what I did.
The table's name is "security" and this is the script:
create table security (
id_emplo int(6) ZEROFILL NOT NULL,
id_boss varchar(10) DEFAULT NULL,
TIP int(5) NOT NULL,
security_division varchar(40),
PRIMARY KEY (id_emplo),
FOREIGN KEY (id_emplo) REFERENCES employees (id_emple) ON DELETE RESTRICT ON UPDATE CASCADE,
FOREIGN KEY (id_boss) REFERENCES security (id_emple) ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=INNODB;
I have another table called "employees". If I try to create the table with only "id_emplo" REFERENCES employess... It doesn't have any problem.
You have to be very careful about types and declarations. The following should work:
create table security (
id_emplo int(6) ZEROFILL NOT NULL,
id_boss int(6) ZEROFILL DEFAULT NULL,
TIP int(5) NOT NULL,
security_division varchar(40),
PRIMARY KEY (id_emplo),
FOREIGN KEY (id_emplo) REFERENCES employees (id_emple) ON DELETE RESTRICT ON UPDATE CASCADE,
FOREIGN KEY (id_boss) REFERENCES security (id_emplo) ON DELETE RESTRICT ON UPDATE CASCADE
);
Here is the SQL Fiddle.
You had two main problems:
You had the name of the column incorrect. It should be id_emplo rather than id_emple (although I give foreign key columns the same name as the column they refer to when I can).
The types were incompatible.

can 2 entities have 2 relationships

In database design, can 2 entities have 2 relationships among themselves? i.e for example there are 2 entities donor and admin.. there are 2 relationships
1. admin accesses donor details
2. admin can contact donor and vice versa
can we join them with 2 relationships?
Definitely, although how much sense it makes to model "accesses" and "contacts" relations in a database depends on your application. I'll stay with your example though and assume these relations are n to n. Here is how the SQL could look like (warning, syntax not tested):
CREATE TABLE admin (
id int unsigned AUTO_INCREMENT PRIMARY KEY,
name text NOT NULL
);
CREATE TABLE donor (
id int unsigned AUTO_INCREMENT PRIMARY KEY,
name text NOT NULL
);
CREATE TABLE admin_donor_access_details (
id_admin int unsigned NOT NULL,
id_donor int unsigned NOT NULL,
PRIMARY KEY (id_admin, id_donor),
CONSTRAINT FOREIGN KEY(id_admin) REFERENCES admin(id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT FOREIGN KEY(id_donor) REFERENCES donor(id) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE TABLE admin_donor_contact (
id_admin int unsigned NOT NULL,
id_donor int unsigned NOT NULL,
PRIMARY KEY (id_admin, id_donor),
CONSTRAINT FOREIGN KEY(id_admin) REFERENCES admin(id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT FOREIGN KEY(id_donor) REFERENCES donor(id) ON DELETE CASCADE ON UPDATE CASCADE
);
The two relations could also be expressed in a single join table with boolean flags, like this:
CREATE TABLE admin_donor (
id_admin int unsigned NOT NULL,
id_donor int unsigned NOT NULL,
detail_access tinyint(1) NOT NULL,
contact tinyint(1) NOT NULL,
PRIMARY KEY (id_admin, id_donor),
CONSTRAINT FOREIGN KEY(id_admin) REFERENCES admin(id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT FOREIGN KEY(id_donor) REFERENCES donor(id) ON DELETE CASCADE ON UPDATE CASCADE
);
This will put some extra effort on your code because you need to determine whether to insert or update a row when adding a relationship, and whether to delete or update a row when removing a relationship, but in my opinion this is still a usable alternative.