MySQL Query Optimization for Analytics - mysql

I've about 30 tables in MySQL Data Warehouse database for analytical data. For now its around 2 Millions of data rows, but I'm sure in future it will become billions very soon. Challenge is, the queries should return the data in a fast way. I've following simple query which is taking over 60 seconds to process those 2 Million rows:
SELECT count(distinct fact.dim_pageview_id)
FROM `datawarehouse_schema_alpha`.`fact_master` fact
left join dim_visit visit on visit.dim_visit_id = fact.dim_visit_id
left join dim_datetime datetim on datetim.dim_datetime_id = fact.dim_datetime_id
where fact.dim_site_id = 552
Explain query result:
1 SIMPLE fact ref fk_fact_bb_pageview_dim_bb_site,fk_fact_master_dim_site fk_fact_bb_pageview_dim_bb_site 5 const 17490 Using where
1 SIMPLE visit eq_ref PRIMARY PRIMARY 4 datawarehouse_schema_alpha.fact.dim_visit_id 1 Using index
1 SIMPLE datetim eq_ref PRIMARY PRIMARY 4 datawarehouse_schema_alpha.fact.dim_datetime_id 1 Using index
I've following sample database structure:
--
-- Table structure for table `dim_datetime`
--
CREATE TABLE IF NOT EXISTS `dim_datetime` (
`dim_datetime_id` int(11) NOT NULL AUTO_INCREMENT,
`datetime_date` varchar(45) CHARACTER SET latin1 DEFAULT NULL,
`datetime_year` varchar(45) CHARACTER SET latin1 DEFAULT NULL,
`datetime_full` varchar(45) CHARACTER SET latin1 DEFAULT NULL,
PRIMARY KEY (`dim_datetime_id`)
) ENGINE=InnoDB DEFAULT CHARSET=big5 AUTO_INCREMENT=4568326 ;
-- --------------------------------------------------------
--
-- Table structure for table `dim_visit`
--
CREATE TABLE IF NOT EXISTS `dim_visit` (
`dim_visit_id` int(11) NOT NULL AUTO_INCREMENT,
`visit_start_time` datetime DEFAULT NULL,
`visit_end_time` datetime DEFAULT NULL,
`visit_duration` varchar(45) DEFAULT NULL,
PRIMARY KEY (`dim_visit_id`)
) ENGINE=InnoDB DEFAULT CHARSET=big5 AUTO_INCREMENT=1295102 ;
-- --------------------------------------------------------
--
-- Table structure for table `dim_site`
--
CREATE TABLE IF NOT EXISTS `dim_site` (
`dim_site_id` int(11) NOT NULL AUTO_INCREMENT,
`site_name` varchar(255) CHARACTER SET latin1 DEFAULT NULL,
`site_url` text CHARACTER SET latin1,
`site_key` text CHARACTER SET latin1,
PRIMARY KEY (`dim_site_id`)
) ENGINE=InnoDB DEFAULT CHARSET=big5 AUTO_INCREMENT=870 ;
--
-- Table structure for table `fact_master`
--
CREATE TABLE IF NOT EXISTS `fact_master` (
`fact_master_id` int(11) NOT NULL AUTO_INCREMENT,
`dim_pageview_id` int(11) DEFAULT NULL,
`dim_visit_id` int(11) DEFAULT NULL,
`dim_site_id` int(11) DEFAULT NULL,
`dim_datetime_id` int(11) DEFAULT NULL,
`master_ip` varchar(255) DEFAULT NULL,
`master_spent_time` varchar(255) DEFAULT NULL,
`master_datetime` datetime DEFAULT NULL,
PRIMARY KEY (`fact_master_id`),
KEY `fk_fact_bb_pageview_dim_bb_visit` (`dim_visit_id`),
KEY `fk_fact_bb_pageview_dim_bb_datetime` (`dim_datetime_id`),
KEY `fk_fact_bb_pageview_dim_bb_pageview` (`dim_pageview_id`),
KEY `fk_fact_master_dim_pageview` (`dim_pageview_id`),
KEY `fk_fact_master_dim_visit` (`dim_visit_id`),
KEY `fk_fact_master_dim_datetime` (`dim_datetime_id`),
KEY `fk_fact_master_dim_site` (`dim_site_id`),
) ENGINE=InnoDB DEFAULT CHARSET=big5 AUTO_INCREMENT=1 ;
--
-- Constraints for dumped tables
--
--
-- Constraints for table `fact_master`
--
ALTER TABLE `fact_master`
ADD CONSTRAINT `fk_fact_master_dim_datetime` FOREIGN KEY (`dim_datetime_id`) REFERENCES `dim_datetime` (`dim_datetime_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
ADD CONSTRAINT `fk_fact_master_dim_pageview` FOREIGN KEY (`dim_pageview_id`) REFERENCES `dim_pageview` (`dim_pageview_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
ADD CONSTRAINT `fk_fact_master_dim_site` FOREIGN KEY (`dim_site_id`) REFERENCES `dim_site` (`dim_site_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
ADD CONSTRAINT `fk_fact_master_dim_visit` FOREIGN KEY (`dim_visit_id`) REFERENCES `dim_visit` (`dim_visit_id`) ON DELETE NO ACTION ON UPDATE NO ACTION;
Please let me know what problem could there be? How can I use Indexes and Views to get the data in a really really quick way? Any other suggestions than using Indexes or Views, to optimize the speed is also welcome. Thanks!

Related

Very Slow result when use WHERE and ORDER BY condition in MYSQL Query

I am facing issue of very slow result.
I am sharing table structure as and results also.
if you have any suggestion please update soon if possible for you.
=================================================================
Table Structure
-- https://www.phpmyadmin.net/
--
-- Host: localhost:3306
-- Generation Time: Mar 25, 2019 at 11:48 AM
-- Server version: 5.5.61-cll
-- PHP Version: 7.2.7
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";
--
-- Database: `energe3c_lms`
--
-- --------------------------------------------------------
--
-- Table structure for table `user_material`
--
CREATE TABLE `user_material` (
`id` int(11) NOT NULL,
`user_course_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`material_id` int(11) NOT NULL,
`attempt` int(11) NOT NULL,
`another_attempt` tinyint(1) DEFAULT '0',
`status` varchar(255) DEFAULT NULL,
`complete` tinyint(1) DEFAULT NULL,
`percent` float DEFAULT '0',
`time` varchar(255) DEFAULT NULL,
`marking_time` varchar(255) DEFAULT NULL,
`marked_by` int(11) DEFAULT NULL,
`feedback` text,
`submitted_date` datetime DEFAULT NULL,
`marking_date` datetime DEFAULT NULL,
`created` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Indexes for dumped tables
--
--
-- Indexes for table `user_material`
--
ALTER TABLE `user_material`
ADD PRIMARY KEY (`id`),
ADD KEY `user_material-user` (`user_id`),
ADD KEY `user_material-material` (`material_id`),
ADD KEY `user_material-marking-user` (`marked_by`),
ADD KEY `user_course-user_material` (`user_course_id`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `user_material`
--
ALTER TABLE `user_material`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
--
-- Constraints for dumped tables
--
--
-- Constraints for table `user_material`
--
ALTER TABLE `user_material`
ADD CONSTRAINT `user_course-user_material` FOREIGN KEY (`user_course_id`) REFERENCES `user_course` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `user_material-marking-user` FOREIGN KEY (`marked_by`) REFERENCES `user` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
ADD CONSTRAINT `user_material-material` FOREIGN KEY (`material_id`) REFERENCES `material` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `user_material-user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
COMMIT;
Showing rows 0 - 24 (25586268 total, Query took 0.0007 seconds.)
SELECT * FROM user_material
=================================================================
Showing rows 0 - 24 (77 total, Query took 22.9434 seconds.)
SELECT * FROM user_material where status='submitted'
=================================================================
Showing rows 0 - 24 (34026300 total, Query took 24.4978 seconds.)
[submitted_date: ... - ...]
SELECT * FROM user_material ORDER BY submitted_date ASC
=================================================================
If You have any suggestion, please update me.
I am using this query in yii2 framework
For this query:
SELECT *
FROM user_material
WHERE status = 'submitted';
You want an index on user_material(status).
For this query:
SELECT *
FROM user_material
ORDER BY submitted_date ASC
You want an index on user_material(submitted_date).
In both these cases, additional columns can be in the index after the one specified as the first column. I do not see these indexes in your schema.
Adding to the recommendation from #Gordon Linoff, you should consider normalizing your table further, to make the SQL more efficient. Use an very short code or int for status_id and store a unique list of status codes and descriptions in a lookup table. Then simply join the lookup table in your SQL. See this article for a detailed explanation on normalization.
I also recommend either changing the column name or the data type for the time column, because it doesn't make sense. What time value requires 255 characters?
If you are concerned about performance, then the first thing to focus your attention on is removing unnecessary bytes, sorts and filters from your query. This approach deals with most performance issues. How you join tables is important as well. As the data volume and complexity increases, the tuning effort becomes increasingly more complex as well.
Getting to know EXPLAIN will help your effort. Refer to the EXPLAIN documentation for more information.
-- Create your lookup table first
CREATE TABLE `material_status` (
`id` int(11) NOT NULL,
`status` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `material_status`
ADD PRIMARY KEY (`id`),
ALTER TABLE `material_status`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
-- Then create your user_material table
CREATE TABLE `user_material` (
`id` int(11) NOT NULL,
`user_course_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`material_id` int(11) NOT NULL,
`attempt` int(11) NOT NULL,
`another_attempt` tinyint(1) DEFAULT '0',
`status_id` int(11) DEFAULT NULL,
`complete` tinyint(1) DEFAULT NULL,
`percent` float DEFAULT '0',
`time` varchar(255) DEFAULT NULL,
`marking_time` varchar(255) DEFAULT NULL,
`marked_by` int(11) DEFAULT NULL,
`feedback` text,
`submitted_date` datetime DEFAULT NULL,
`marking_date` datetime DEFAULT NULL,
`created` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- Create your extra index here
CREATE INDEX ix-submitted_date ON user_material(submitted_date);
-- Add the status to the keys and constraints
ALTER TABLE `user_material`
ADD PRIMARY KEY (`id`),
ADD KEY `user_material-user` (`user_id`),
ADD KEY `user_material-material` (`material_id`),
ADD KEY `user_material-marking-user` (`marked_by`),
ADD KEY `user_course-user_material` (`user_course_id`),
ADD KEY `user_material-status` (`status_id`);
ALTER TABLE `user_material`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
ALTER TABLE `user_material`
ADD CONSTRAINT `user_course-user_material` FOREIGN KEY (`user_course_id`) REFERENCES `user_course` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `user_material-marking-user` FOREIGN KEY (`marked_by`) REFERENCES `user` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
ADD CONSTRAINT `user_material-material` FOREIGN KEY (`material_id`) REFERENCES `material` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `user_material-user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `user_material-status` FOREIGN KEY (`status_id`) REFERENCES `material_status` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
The queries will now look like this:
SELECT
um.id,
um.user_course_id,
um.user_id,
um.material_id,
um.attempt,
um.another_attempt,
ms.status,
um.complete,
um.percent,
um.time,
um.marking_time,
um.marked_by,
um.feedback,
um.submitted_date,
um.marking_date,
um.created
FROM user_material um
INNER JOIN material_status ms on ms.id = um.status_id
WHERE ms.status = 'submitted';
SELECT
um.id,
um.user_course_id,
um.user_id,
um.material_id,
um.attempt,
um.another_attempt,
ms.status,
um.complete,
um.percent,
um.time,
um.marking_time,
um.marked_by,
um.feedback,
um.submitted_date,
um.marking_date,
um.created
FROM user_material um
INNER JOIN material_status ms on ms.id = um.status_id
ORDER BY um.submitted_date ASC;
Please try this.
SELECT
um.id,
um.user_course_id,
um.user_id,
um.material_id,
um.attempt,
um.another_attempt,
ms.status,
um.complete,
um.percent,
um.time,
um.marking_time,
um.marked_by,
um.feedback,
um.submitted_date,
um.marking_date,
um.created
FROM user_material um
INNER JOIN material_status ms on ms.id = um.status_id
AND ms.status = 'submitted';
And also create indexing on where and order field.

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 InnoDB: FOREIGN KEY constraint performance

I have the following InnoDB tables:
CREATE TABLE `vehicle` (
`ID` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`Name` varchar(50) DEFAULT NULL,
`Model` varchar(100) DEFAULT NULL,
`Engine_Type` varchar(70) DEFAULT NULL,
`Construction_From` date DEFAULT NULL,
`Construction_To` date DEFAULT NULL,
`Engine_Power_KW` mediumint(8) unsigned DEFAULT NULL,
`Engine_Power_HP` mediumint(8) unsigned DEFAULT NULL,
`CC` mediumint(8) unsigned DEFAULT NULL,
`TTC_TYP_ID` int(11) unsigned DEFAULT NULL,
`Vehicle_Type` tinyint(1) DEFAULT NULL,
`ID_Body_Type` tinyint(3) unsigned DEFAULT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=49407 DEFAULT CHARSET=utf8;
CREATE TABLE `part` (
`ID` int(11) unsigned NOT NULL AUTO_INCREMENT,
`ID_Brand` smallint(5) unsigned DEFAULT NULL,
`Code_Full` varchar(50) DEFAULT NULL,
`Code_Condensed` varchar(50) DEFAULT NULL,
`Ean` varchar(50) DEFAULT NULL COMMENT 'The part barcode.',
`TTC_ART_ID` int(11) unsigned DEFAULT NULL COMMENT 'TecDoc ID.',
`ID_Product_Status` tinyint(3) unsigned DEFAULT NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `TTC_ART_ID_UNIQUE` (`TTC_ART_ID`),
UNIQUE KEY `ID_Brand_Code_Full_UNIQUE` (`ID_Brand`,`Code_Full`)
) ENGINE=InnoDB AUTO_INCREMENT=3732260 DEFAULT CHARSET=utf8;
CREATE TABLE `vehicle_part` (
`ID_Vehicle` mediumint(8) unsigned NOT NULL,
`ID_Part` int(11) unsigned NOT NULL,
PRIMARY KEY (`ID_Vehicle`,`ID_Part`),
KEY `fk_vehicle_part_vehicle_id_vehicle_idx` (`ID_Vehicle`),
KEY `fk_vehicle_part_part_id_part_idx` (`ID_Part`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Table vehicle has about 45.000 records, table part has about 3.500.000 records and table vehicle_part has approximately 100.000.000 records.
Creating the secondary indexes for vehicle_part did not take too long, about 30 min for both.
What I cannot do though is create the foreign key constraints: for example
ALTER TABLE `vehicle_part`
ADD CONSTRAINT `fk_vehicle_part_vehicle_id_vehicle`
FOREIGN KEY (`ID_Vehicle`)
REFERENCES `vehicle` (`ID`)
ON DELETE NO ACTION
ON UPDATE NO ACTION;
takes ages to complete. I understand the table is rebuilt since it consumes a lot of disk space. What can I do to improve the performance?
If I create the table with the fk constraints and then add the records the insert process in vehicle_part also takes ages (about 3 days).
I am using a laptop with 4GB RAM.
EDIT 12/01/2016
The answer given by Drew helped a lot in improving the performance dramatically. I changed every script using SELECT ... INTO outfile and then LOAD DATA INFILE from the exported csv file. Also sometimes before LOAD DATA INFILE dropping the indexes and recreating them after the load proccess saves even more time. There is no need to drop the fk constraints just the secondary indexes.
If you know your data is pristine from an FK perspective, then establish your structure without secondary indexes as suggested in comments, but with the FK in the schema yet with FK checks temporarily disabled.
Load your data. If external data, certainly do it with LOAD DATA INFILE.
After your data is loaded, turn on FK checks. And establish secondary indexes with Alter Table.
Again, going with the assumption that your data is clean. There are other ways of proving that after-the-fact for the risk-adverse.
create table student
( id int auto_increment primary key,
sName varchar(100) not null
-- secondary indexes to be added later
);
create table booksAssigned
( id int auto_increment primary key,
studentId int not null,
isbn varchar(20) not null,
constraint foreign key `fk_b_s` (studentId) references student(id)
-- secondary indexes to be added later
);
insert booksAssigned(studentId,isbn) values (1,'asdf'); -- Error 1452 as expected
set FOREIGN_KEY_CHECKS=0; -- turn FK checks of temporarily
insert booksAssigned(studentId,isbn) values (1,'asdf'); -- Error 1452 as expected
set FOREIGN_KEY_CHECKS=1; -- succeeds despite faulty data
insert booksAssigned(studentId,isbn) values (2,'38383-asdf'); -- Error 1452 as expected
As per op comments, how to drop auto-generated index in referencing table after initial schema creation:
mysql> show create table booksAssigned;
| booksAssigned | CREATE TABLE `booksassigned` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`studentId` int(11) NOT NULL,
`isbn` varchar(20) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_b_s` (`studentId`),
CONSTRAINT `booksassigned_ibfk_1` FOREIGN KEY (`studentId`) REFERENCES `student` (`id`)
) ENGINE=InnoDB |
mysql> set FOREIGN_KEY_CHECKS=0;
Query OK, 0 rows affected (0.00 sec)
mysql> drop index `fk_b_s` on booksAssigned;
Query OK, 0 rows affected (0.49 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show create table booksAssigned;
| booksAssigned | CREATE TABLE `booksassigned` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`studentId` int(11) NOT NULL,
`isbn` varchar(20) NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `booksassigned_ibfk_1` FOREIGN KEY (`studentId`) REFERENCES `student` (`id`)
) ENGINE=InnoDB |
Further links
Temporarily disable foreign keys
A Rolando Answer

MySQL InnoDB FOREIGN KEY ERROR

I have the followiing tables:
CREATE TABLE `Atletica` (
`Universidade` varchar(100) NOT NULL,
`Nome` varchar(100) NOT NULL,
`Logo` varchar(100) NOT NULL,
`GritoDeGuerra` varchar(100) NOT NULL,
`EnderecoCEP` int(11) NOT NULL,
`EnderecoNumero` int(11) NOT NULL,
`MedalhaOuro` int(6) NOT NULL,
`MedalhaPrata` int(6) NOT NULL,
`MedalhaBronze` int(6) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- --------------------------------------------------------
--
-- Table structure for table `Endereco`
--
CREATE TABLE `Endereco` (
`Rua` varchar(50) NOT NULL,
`Numero` int(11) NOT NULL,
`Bairro` varchar(50) DEFAULT NULL,
`CEP` int(11) NOT NULL,
`Cidade` varchar(50) NOT NULL,
`Estado` varchar(50) NOT NULL,
`Complemento` varchar(50) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Indexes for dumped tables
--
--
-- Indexes for table `Atletica`
--
ALTER TABLE `Atletica`
ADD PRIMARY KEY (`Universidade`,`Nome`),
ADD KEY `EnderecoCEP` (`EnderecoCEP`),
ADD KEY `EnderecoNumero` (`EnderecoNumero`);
--
-- Indexes for table `Endereco`
--
ALTER TABLE `Endereco`
ADD PRIMARY KEY (`Numero`,`CEP`);
And I keep getting the error:
Error creating foreign key on EnderecoCEP, EnderecoNumero (check data types)
when I try to execute the following command:
ALTER TABLE `Atletica`
ADD FOREIGN KEY (`EnderecoCEP`, `EnderecoNumero`)
REFERENCES `proj3`.`Endereco`(`CEP`, `Numero`)
ON DELETE RESTRICT ON UPDATE RESTRICT;
Ive read tons of similar questions here but all of them the error was an obvious data type mismatch. I only have those two table on my database. Please help.
Thank you very much for your time.
As it turns out, when you create a foreign key with multiple columns, it should be in the same order ar the primary key in the referencing table.

mysql won't allow foreign key

Many people had this problem already, but there was no fitting solution in other posts.
I have two tables, one named "sales", the other named "host_flags". I would like to have a foreign key for host_flags.sales_id to sales.id, but mysql won't let me! I have primary indexes defined in each table, so I wonder why...
The host_flags table already has a foreign key on the column host_id, but even when I tried and created the foreign key for the sales id first, it wouldn't let me.
The tables look like:
CREATE TABLE `sales` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`email` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`creation` datetime DEFAULT NULL,
`lastupdate` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
CREATE TABLE `host_flags` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`host_id` int(11) DEFAULT NULL,
`sales_id` int(11) DEFAULT NULL,
`creation` datetime DEFAULT NULL,
`lastupdate` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `host_id6` (`host_id`),
CONSTRAINT `host_id6` FOREIGN KEY (`host_id`) REFERENCES `hosts` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `hosts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`creation` datetime NOT NULL,
`lastupdate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=32225 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
I get this error message:
MySQL said: Can't create table 'primarydata.#sql-191_1' (errno: 150)
Thanks!
Charles
SOLUTION FOUND
All ints of the primary indexes have to be either signed or unsigned - not mixed.
Typically:
I like to declare the FK constraints outside of the table definition after all tables have been constructed.
ALTER TABLE `tbl`
ADD CONSTRAINT `constr`
FOREIGN KEY `fk_id` REFERENCES `ftbl`(`id`)
ON UPDATE CASCADE
ON DELETE CASCADE;
This way I can make sure the problem isn't something like the datatype of tbl.fk_id not being the same as the one of ftbl.id (including UNSIGNED as #Devart said). Or not having declared ftbl.id as unique. Regardless of the order of declaration of the tables.
After i do this i can integrate the constraint back into the table definition and take into account the order in which the tables need to be created to allow the constraint to be added.
You problem:
-- creating the sales table
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
-- creating the host_flags table
`sales_id` int(11) DEFAULT NULL,
-- the sales.id is declared as unsigned
-- the host_flags.sales_id is declared signed
Additonally to Recursed's answer:
There is a requirement that foreign keys contstraints' names must be unique in database scope. Maybe changing the name will work?
There is also a huge thread on MySQL community forums about the problem containing several solutions for some specific situations.
Possible two errors:
Reference and referenced columns must have the same type - int(11) unsigned
Unknown referenced table hosts.