Help with my table structure - mysql

I'm trying to make an address book. And have made my tables like this:
CREATE TABLE `list_`.`contacts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` tinyint(11) NOT NULL,
`group` varchar(128) NOT NULL,
`first_name` varchar(128) NOT NULL,
`last_name` varchar(128) NOT NULL,
`address` varchar(128) NOT NULL,
`city` varchar(128) NOT NULL,
`state` varchar(2) NOT NULL,
`zip` int(5) NOT NULL,
`phone_number` varchar(16) NOT NULL,
`cell_number` varchar(16) NOT NULL,
`work_number` varchar(16) NOT NULL,
`fax_number` varchar(16) NOT NULL,
`email` varchar(128) NOT NULL,
`company` varchar(55) NOT NULL,
`title` varchar(56) NOT NULL,
`notes` text NOT NULL,
`date_added` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`,`user_id`),
KEY `user_id` (`user_id`),
KEY `group` (`group`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 AUTO_INCREMENT=9 ;
CREATE TABLE `list_`.`groups` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` tinyint(11) NOT NULL,
`position` int(8) unsigned NOT NULL DEFAULT '0',
`name` varchar(128) NOT NULL,
`date_added` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`,`user_id`),
KEY `user_id` (`user_id`),
KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8 AUTO_INCREMENT=32 ;
My logic here is that I have all the contacts in the contacts table, from there I have a column called group that I use to filter the contacts into groups.
Then I have a table called groups that I'll use to keep track of what groups where created by a specific user and fill a with these groups so they can move the contacts around.
When a group is deleted I will throw back an error no letting it be deleted if the group contains contacts. I can probably query the contacts to see what group they belong to and if they belong to the group being deleted then I'll move them into a group called Uncategorized or something.
But if they choose to accept and delete ALL contacts within a group then go ahead and delete the group and all child rows that belong to that group.
I am having trouble creating my Foreing Keys in the contacts table. No matter what combination of index and keys I try I still can't make it work.
--
-- Constraints for table `contacts`
--
ALTER TABLE `list_`.`contacts`
ADD CONSTRAINT `contacts_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`),
ADD CONSTRAINT `contacts_ibfk_2` FOREIGN KEY (`group`) REFERENCES `groups` (`name`) ON UPDATE CASCADE;
--
-- Constraints for table `groups`
--
ALTER TABLE `list_`.`groups`
ADD CONSTRAINT `group_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`);
Also, can someone help me with the ON DELETE and ON UPDATE. To help me figure out how I can delete all child rows that reference the name column in groups

I do not think you should have MySQL do so much heavy lifting, specifically deleting a group if it is empty. Is there any specific reason to do this, i.e. can the group no longer be used if it is empty? You could probably achieve this with mysql triggers if you really wanted to.
As for preventing the deletion of a group that is simply done by the foriegn key on contacts that you already have on groups. However I highly recommend that you use ids rather than names for keys.
What you are saying seems to conflict, however: you want to prevent deletion of a group that has contacts, but you want all contacts in a group to be deleted when the group is deleted?

Related

Should I use Inner Join in this case?

I am working on a student attendance mini-project, and I don't know how to proceed for my database. I'm new to SQL and databases in general so this might seem dumb to you.
So, I want to do a database containing the table student, which contains : student_id (primary key) , name (string) and attendance(boolean) (that's the bare minimum, i'll add more afterwards) and I want to register the daily attendance of the students. So I want to have all the students tied to every date of the week.
I created a date table in phpMyadmin but I don't know how to proceed to link them, i've tried an Inner Join and it was successful.
The problem is : If i want to add another line to the student table my table won't update, so is there a way to "automatically" tie all the students to the date table ?
Sorry if this seems confused I've tried my best to summarize it !
Lets have some idea about tables should be there to implement a proper Student Attendance system in place. I have copied create script for some of my tables that used for maintaining Students record per course. I hope following sample Table scripts with relation will help you understanding regarding table structure and also to solve your issue.
Please be noted, That this table structures for your your reference only. You can add/remove tables/columns as per your requirement once you get an overall idea from this post.
CREATE TABLE `staff` (
`id` int(11) NOT NULL,
`type` varchar(200) DEFAULT NULL,
`first_name` varchar(200) DEFAULT NULL,
`last_name` varchar(200) DEFAULT NULL,
`emal` varchar(200) DEFAULT NULL,
`contact` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `batch` (
`id` int(11) NOT NULL,
`department` varchar(200) DEFAULT NULL,
`details` varchar(200) DEFAULT NULL,
`staff_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `batch_staff_idx` (`staff_id`),
CONSTRAINT `batch_staff` FOREIGN KEY (`staff_id`) REFERENCES `staff` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `student` (
`id` int(11) NOT NULL,
`batch_id` int(11) DEFAULT NULL,
`first_name` varchar(200) DEFAULT NULL,
`last_name` varchar(200) DEFAULT NULL,
`email` varchar(200) DEFAULT NULL,
`contact` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `batch_student_idx` (`batch_id`),
CONSTRAINT `batch_student` FOREIGN KEY (`batch_id`) REFERENCES `batch` (`batch_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `course` (
`id` int(11) NOT NULL,
`name` varchar(200) DEFAULT NULL,
`details` varchar(200) DEFAULT NULL,
`staff_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `course_staff_idx` (`staff_id`),
CONSTRAINT `course_staff` FOREIGN KEY (`staff_id`) REFERENCES `staff` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `attendence` (
`course_id` int(11) NOT NULL,
`student_id` int(11) DEFAULT NULL,
`class_date` date DEFAULT NULL,
KEY `att_course_idx` (`course_id`),
KEY `att_student_idx` (`student_id`),
CONSTRAINT `att_course` FOREIGN KEY (`course_id`) REFERENCES `course` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `att_student` FOREIGN KEY (`student_id`) REFERENCES `student` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Your required data will finally store into table Attendance. From this table data, you will be able to find list of students absent/present per date and per course. Remember, the attendance table should enrich daily from a automated OR a manual process.

MySQL Temporary Table with Group By and Group Concat Extremely Slow

I'm trying to build out a fairly simple temporary table. The table will end up being 2 columns:
1 A product ID
2 A string of concatenated compliance data in a format that can be consumed later
CREATE TEMPORARY TABLE products.compliances_data
(INDEX product_id_idx (product_id))
SELECT
products.product_id,
GROUP_CONCAT(JSON_OBJECT('compliance_code', cc.compliance_code, 'compliance_full_name', pc.full_name, 'compliance_web_description_short', pc.web_description_short) SEPARATOR ' - ') as compliances
FROM products.products products
LEFT OUTER JOIN products.material_compliance_map cc on products.material_id = cc.material_id
LEFT OUTER JOIN products.compliances pc on cc.compliance_id = pc.compliance_id
GROUP BY products.part_number
The table gathers and joins information from 3 tables.
The products table is the main source of information. The products table has a column for material_id.
The material_compiance_map table. This is a many-to-many table that maps material_id's to compliance_id's
The compliances table. This is the table where the actual data is stored that I need to pull from to build out the concatenated object.
The problem is the creation of the temporary table takes 3-4 minutes to run yet only yields about 1.2 million entries which seems incredibly slow.
Running an explain on the select portion of the query yields:
Explain on select image
There are only 642 entries in the material_comliance_map so there's not an awful lot of data here that needs to be traversed.
I've tried removing the group_concat and that seems to speed up the query about 33%. The problem seems to revolve around the group by statement.
How can I improve the speed when building this temp table?
EDIT:
Schemas:
material_compliance_map schema
'material_compliance_map'
CREATE TABLE `material_compliance_map` (
`material_id` int(11) NOT NULL,
`material_code` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
`compliance_id` int(11) NOT NULL,
`compliance_code` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`material_id`,`compliance_id`),
KEY `fk_compliance_id_material_compliances_compliances` (`compliance_id`),
KEY `fk_material_id_material_compliances_materials` (`material_id`),
CONSTRAINT `fk_compliance_id_material_compliances_compliances` FOREIGN KEY (`compliance_id`) REFERENCES `compliances` (`compliance_id`) ON UPDATE CASCADE,
CONSTRAINT `fk_material_id_material_compliances_materials` FOREIGN KEY (`material_id`) REFERENCES `materials` (`material_id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
compliances schema:
'compliances'
CREATE TABLE `compliances` (
`compliance_id` int(11) NOT NULL AUTO_INCREMENT,
`compliance_code` varchar(20) NOT NULL,
`full_name` varchar(155) DEFAULT NULL,
`web_description_short` varchar(45) DEFAULT NULL,
PRIMARY KEY (`compliance_id`),
UNIQUE KEY `compliance_id_UNIQUE` (`compliance_id`),
UNIQUE KEY `compliance_code_UNIQUE` (`compliance_code`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=latin1
products schema:
'products'
CREATE TABLE `products` (
`part_number` varchar(27) NOT NULL,
`material_code` varchar(30) DEFAULT NULL,
`material_id` int(11) DEFAULT NULL,
`size_code` varchar(15) DEFAULT NULL,
`size_id` int(11) DEFAULT NULL,
`erp_description_1` varchar(31) DEFAULT NULL,
`erp_description_2` varchar(31) DEFAULT NULL,
`search_description` varchar(250) DEFAULT NULL,
`weight_lbs` decimal(8,4) DEFAULT NULL,
`part_number_prefix` varchar(15) DEFAULT NULL,
`tight_tolerance` tinyint(4) DEFAULT NULL,
`product_id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`product_id`),
UNIQUE KEY `part_number_UNIQUE` (`part_number`),
UNIQUE KEY `product_id_UNIQUE` (`product_id`),
KEY `fk_material_id_products_materials` (`material_id`),
KEY `fk_size_id_products_sizes` (`size_id`),
KEY `product_id` (`product_id`),
KEY `size_id_idx` (`size_id`),
KEY `size_id_productsidx` (`size_id`),
KEY `material_id_idx` (`material_id`),
CONSTRAINT `fk_material_id_products_materials` FOREIGN KEY (`material_id`) REFERENCES `materials` (`material_id`),
CONSTRAINT `fk_size_id_products_sizes` FOREIGN KEY (`size_id`) REFERENCES `sizes` (`size_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1140987 DEFAULT CHARSET=latin1

MySQL - delete rows rejected on INNODB table

I have two tables - a user table and a userlog table.
CREATE TABLE `client_user` (
`id_client_user` int(11) NOT NULL auto_increment,
`Nom` varchar(45) NOT NULL,
`Prenom` varchar(45) NOT NULL,
`email` varchar(255) NOT NULL,
`userid` varchar(45) NOT NULL,
`password` varchar(45) NOT NULL,
`active` tinyint(1) NOT NULL default '0',
`lastaccess` timestamp NULL default NULL,
`user_must_change_pwd` tinyint(1) NOT NULL default '0',
PRIMARY KEY (`id_client_user`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `user_log` (
`id_user_log` int(11) NOT NULL auto_increment,
`access` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
`zone_updated` varchar(255) NOT NULL,
`id_client_user` int(11) NOT NULL,
PRIMARY KEY (`id_user_log`),
KEY `fk_user_log_client_user1` (`id_client_user`),
CONSTRAINT `fk_user_log_client_user1`
FOREIGN KEY (`id_client_user`)
REFERENCES `client_user` (`id_client_user`)
ON DELETE NO ACTION
ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
I create a user in the client_user table and then his activity is logged within the user_log table.
I now need to delete rows in the user_log table.
This is rejected because of the foreign key constraint - that much I have understood.
After having looked at the documentation, I have not seen how I can change the foreign key to allow me to delete the user_log records.
What I need is a foreign key (1:n), client_user (1) to user_log (n), where user_log records can be deleted without impacting the associated client_user record.
I am sure that this is possible with innodb, but I cannot see how.
Help ?
From the specification
InnoDB supports the use of ALTER TABLE to drop foreign keys:
ALTER TABLE tbl_name DROP FOREIGN KEY fk_symbol;

Change master table PK and update related table FK (changing PK from Autoincrement to UUID on Mysql)

I have two related tables: Groups and Clients. Clients belongs to Groups so I have a foreign key "group_id" that references the group a client belongs to.
I'm changing the Group id from an autoincrement to a UUID. So what I need is to generate a UUID for each Group and update the Clients table at once to reflect the changes and keep the records related.
Is there a way to do this with multiple-table update on MySQL?
Adding tables definitions for clarification.
CREATE TABLE `groups` (
`id` char(36) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`created` datetime DEFAULT NULL,
`modified` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8$$
CREATE TABLE `clients` (
`id` char(36) NOT NULL,
`name` varchar(255) NOT NULL,
`group_id` char(36) DEFAULT NULL,
`active` tinyint(1) DEFAULT '1'
PRIMARY KEY (`id`),
KEY `fkgp` (`group_id`),
CONSTRAINT `fkgp` FOREIGN KEY (`group_id`) REFERENCES `groups` (`id`)
ON DELETE NO ACTION ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8$$

Optimize Join sentence with foreign keys, and show records with nulls

I have the following structure
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
CREATE TABLE IF NOT EXISTS `sis_param_tax` (
`id` int(5) NOT NULL auto_increment,
`description` varchar(50) NOT NULL,
`code` varchar(5) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=7;
CREATE TABLE IF NOT EXISTS `sis_param_city` (
`id` int(4) NOT NULL auto_increment,
`name` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
CREATE TABLE IF NOT EXISTS `sis_supplier` (
`id` int(15) NOT NULL auto_increment,
`name` varchar(200) NOT NULL,
`address` varchar(200) default NULL,
`phone` varchar(30) NOT NULL,
`fk_city` int(11) default NULL,
`fk_tax` int(11) default NULL,
PRIMARY KEY (`id`),
KEY `fk_city` (`fk_city`),
KEY `fk_tax` (`fk_tax`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;
ALTER TABLE `sis_supplier`
ADD CONSTRAINT `sis_supplier_ibfk_4` FOREIGN KEY (`fk_tax`) REFERENCES `sis_param_tax` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
ADD CONSTRAINT `sis_supplier_ibfk_3` FOREIGN KEY (`fk_city`) REFERENCES `sis_param_city` (`id`) ON DELETE SET NULL ON UPDATE CASCADE;
My questions are
1. This structure allows me to have a supplier with city and tax fields = null (in case user didn't set these values). Right?
2. If I delete "X" city, supplier's fk_city with city="X" are set to null, same with fk_tax. Right?
3. I want to optimize (IF POSSIBLE) the following join sentence, so I can show suppliers whom have fk_city and/or fk_tax = NULL
SELECT DISTINCT
sis_supplier.id,
sis_supplier.name,
sis_supplier.telefono,
sis_supplier.address,
sis_supplier.phone,
sis_supplier.cuit,
sis_param_city.name AS city,
sis_param_tax.description AS tax,
sis_supplier.fk_city,
sis_supplier.fk_tax
FROM
sis_supplier
LEFT OUTER JOIN sis_param_city
ON
sis_supplier.`fk_city` = sis_param_city.id
LEFT OUTER JOIN `sis_param_tax`
ON
sis_supplier.`fk_tax` = `sis_param_tax`.`id`
Thanks a lot in advance,
Yes.
Yes.
Yes, it's good to optimize. The query you showed looks fine. How is it not working for you?
Have you analyzed the query with EXPLAIN? This can help you tell when you have a query that isn't using indexes effectively. In fact, all of Chapter 7 Optimization would be recommended reading.
if you want to show records with nulls than use RIGHT or LEFT JOIN
depend on your needs