How to optimize this mysql query - explain output included - mysql

This is the query (a search query basically, based on tags):-
select
SUM(DISTINCT(ttagrels.id_tag in (2105,2120,2151,2026,2046) )) as key_1_total_matches, td.*, u.*
from Tutors_Tag_Relations AS ttagrels
Join Tutor_Details AS td ON td.id_tutor = ttagrels.id_tutor
JOIN Users as u on u.id_user = td.id_user
where (ttagrels.id_tag in (2105,2120,2151,2026,2046)) group by td.id_tutor HAVING key_1_total_matches = 1
And following is the database dump needed to execute this query:-
CREATE TABLE IF NOT EXISTS `Users` (
`id_user` int(10) unsigned NOT NULL auto_increment,
`id_group` int(11) NOT NULL default '0',
PRIMARY KEY (`id_user`),
KEY `Users_FKIndex1` (`id_group`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=730 ;
INSERT INTO `Users` (`id_user`, `id_group`) VALUES
(303, 1);
CREATE TABLE IF NOT EXISTS `Tutor_Details` (
`id_tutor` int(10) unsigned NOT NULL auto_increment,
`id_user` int(10) NOT NULL default '0',
PRIMARY KEY (`id_tutor`),
KEY `Users_FKIndex1` (`id_user`),
KEY `id_user` (`id_user`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=58 ;
INSERT INTO `Tutor_Details` (`id_tutor`, `id_user`) VALUES
(26, 303);
CREATE TABLE IF NOT EXISTS `Tags` (
`id_tag` int(10) unsigned NOT NULL auto_increment,
`tag` varchar(255) default NULL,
PRIMARY KEY (`id_tag`),
UNIQUE KEY `tag` (`tag`),
KEY `id_tag` (`id_tag`),
KEY `tag_2` (`tag`),
KEY `tag_3` (`tag`),
KEY `tag_4` (`tag`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2957 ;
INSERT INTO `Tags` (`id_tag`, `tag`) VALUES
(2026, 'Brendan.\nIn'),
(2046, 'Brendan.'),
(2105, 'Brendan'),
(2120, 'Brendan''s'),
(2151, 'Brendan)');
CREATE TABLE IF NOT EXISTS `Tutors_Tag_Relations` (
`id_tag` int(10) unsigned NOT NULL default '0',
`id_tutor` int(10) unsigned default NULL,
`tutor_field` varchar(255) default NULL,
`cdate` timestamp NOT NULL default CURRENT_TIMESTAMP,
`udate` timestamp NULL default NULL,
KEY `Tutors_Tag_Relations` (`id_tag`),
KEY `id_tutor` (`id_tutor`),
KEY `id_tag` (`id_tag`),
KEY `id_tutor_2` (`id_tutor`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `Tutors_Tag_Relations` (`id_tag`, `id_tutor`, `tutor_field`, `cdate`, `udate`) VALUES
(2105, 26, 'firstname', '2010-06-17 17:08:45', NULL);
ALTER TABLE `Tutors_Tag_Relations`
ADD CONSTRAINT `Tutors_Tag_Relations_ibfk_2` FOREIGN KEY (`id_tutor`) REFERENCES `Tutor_Details` (`id_tutor`) ON DELETE NO ACTION ON UPDATE NO ACTION,
ADD CONSTRAINT `Tutors_Tag_Relations_ibfk_1` FOREIGN KEY (`id_tag`) REFERENCES `Tags` (`id_tag`) ON DELETE NO ACTION ON UPDATE NO ACTION;
What the query does?
This query actually searches tutors which contain "Brendan"(as their name or biography or something). The id_tags 2105,2120,2151,2026,2046 are nothing but the tags which are LIKE "%Brendan%".
My question is :-
1.In the explain of this query, the reference column shows NULL for ttagrels, but there are possible keys (Tutors_Tag_Relations,id_tutor,id_tag,id_tutor_2). So, why is no key being taken. How to make the query take references. Is it possible at all?
2. The other two tables td and u are using references. Any indexing needed in those? I think not.
Check the explain query output here
http://www.test.examvillage.com/explain.png

Don't analyze performance of database with single record in table. Create at least 100 records.

Related

Insert into a table with forign key within the trigger of the other table in MySQL

I have a parent table in MySQL as follows:
CREATE TABLE `parentTBL` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`source_name` varchar(255) DEFAULT NULL,
`loc1` smallint(5) unsigned DEFAULT NULL,
`loc2` smallint(5) unsigned DEFAULT NULL,
`extra_info` json DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=latin1;
a child table:
CREATE TABLE `childTBL` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`source_names_id` int(10) unsigned NOT NULL,
`locations_id` int(10) unsigned NOT NULL,
`extra_info` json DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fk_childTBL1_idx` (`source_names_id`),
KEY `fk_childTBL2_idx` (`locations_id`),
CONSTRAINT `fk_childTBL1` FOREIGN KEY (`source_names_id`) REFERENCES `source_names` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `fk_childTBL2` FOREIGN KEY (`locations_id`) REFERENCES `locations` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
and these 2 tables:
CREATE TABLE `source_names` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`source_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `source_name_UNIQUE` (`source_name`)
) ENGINE=InnoDB AUTO_INCREMENT=85 DEFAULT CHARSET=latin1;
CREATE TABLE `locations` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`loc1` smallint(5) unsigned DEFAULT NULL,
`loc2` smallint(5) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `loc1` (`loc1`,`loc2`)
) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=latin1;
I have a trigger for parentTBL table before insert like this:
CREATE DEFINER=`root`#`localhost` TRIGGER `myDB`.`parentTBL_BEFORE_INSERT` BEFORE INSERT ON `parentTBL` FOR EACH ROW
BEGIN
INSERT IGNORE INTO source_names (source_name) VALUES (NEW.source_name);
INSERT IGNORE INTO locations (loc1,loc2) VALUES (NEW.loc1,NEW.loc2);
INSERT IGNORE INTO childTBL (source_names_id,locations_id,extra_info) VALUES (????,????,NEW.extra_info);
END
and I want to insert into childTBL and 2 other tables before inserting into parentTBL but I don't know what should I put in ???? fields in above trigger (third INSERT IGNORE. Move scroll to right)?
(It should be noted that I don't want to use SELECT because I have some millions records and fast insertion is important to me and also my problem is not solved with LAST_INSERT_ID() because it is possible that I need the source_names_id which is added before last_inserted in source_names table.)

MariaDB: Multiple joins and where clause searching for 1 in TINYINT Column not working

I have tables called person and book and image and bookhit.
Person has id, name and Book has id, owner_id, info and Image has a column for id, owner_id, url and thumbnail which is a TINYINT (In the database half the rows are 0s and 1s.) By the way, the image column stores images of the cover of the book in two version: big-one and thumbnail. The table bookhit stores the times the book has been retrieved from the database and has a column hits.
So I tried multiple INNER JOIN to retrieve all the thumbnails for the most popular books. The SQL Query is the following:
SELECT `imagehit`.`hits`, `person`.`name`, `book`.`info`, `image`.`url`, `image`.`thumbnail` FROM `imagehit`
INNER JOIN `person` ON `person`.`id`=`book`.`owner_id`
INNER JOIN `image` ON `image`.`owner_id`=`book`.`id`
ORDER BY `imagehit`.`hits` DESC
WHERE `image`.`thumbnail`=1
LIMIT 10;
And that doesn't work, even though half rows has 1s in image.thumbnail . If I change the following line:
WHERE `image`.`thumbnail`=1
To
WHERE `image`.`thumbnail`=0
It does work. Well, I went to the image table and did a simple query like the following:
SELECT * FROM `image` WHERE `image`.`thumbnail`=0;
And gave me total rows stored in the table. But when I browse image table in phpMyAdmin I see there are 1s stored in the table. :(
Any ideas why this happens? thank you in advance.
Table definitions:
CREATE TABLE `image` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`owner_id` int(11) NOT NULL,
`thumbnail` tinyint(1) NOT NULL,
`url` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `image_url` (`url`),
KEY `image_owner_id` (`owner_id`),
CONSTRAINT `image_ibfk_1` FOREIGN KEY (`owner_id`) REFERENCES `book` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1450 DEFAULT CHARSET=utf8
CREATE TABLE `person` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`url` varchar(60) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `person_url` (`url`),
) ENGINE=InnoDB AUTO_INCREMENT=6287 DEFAULT CHARSET=utf8
CREATE TABLE `book` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`owner_id` int(11) NOT NULL,
`book` varchar(3000) NOT NULL,
`info` varchar(3000) NOT NULL,
`url` varchar(60) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `book_url` (`url`),
KEY `book_owner_id` (`owner_id`),
CONSTRAINT `book_ibfk_1` FOREIGN KEY (`owner_id`) REFERENCES `person` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=725 DEFAULT CHARSET=utf8
CREATE TABLE `imagehit` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`owner_id` int(11) NOT NULL,
`person_id` int(11) NOT NULL,
`hits` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `imagehit_person_id` (`person_id`),
KEY `imagehit_owner_id` (`owner_id`),
KEY `hits` (`hits`),
CONSTRAINT `imagehit_ibfk_1` FOREIGN KEY (`owner_id`) REFERENCES `image` (`id`),
CONSTRAINT `imagehit_ibfk_2` FOREIGN KEY (`person_id`) REFERENCES `person` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=725 DEFAULT CHARSET=utf8
Proof I'm not crazy:
I inserted the data using Peewee, when I created the row I set thumbnail=True if the image was a thumbnail and as thumbnail=False if it wasn't. The column thumbnail is the field BooleanField in Peewee.

Update MySQL: Duplicate unique key #1062

I have a table like this:
CREATE TABLE IF NOT EXISTS `activity` (
`ID` int(8) NOT NULL AUTO_INCREMENT,
`action` tinyint(1) NOT NULL,
`userID` int(8) NOT NULL,
`categoryID` int(8) NOT NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `UNIQUE` (`userID`,`categoryID`),
KEY `categoryID` (`categoryID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=2 ;
With the next information:
INSERT INTO `activity` (`ID`, `action`, `userID`, `categoryID`) VALUES
(1, 2, 11, 312);
I'm trying to execute this query:
UPDATE `activity` SET `action` = '3' WHERE `userID` = '11' AND `categoryID` = '312' ;
And response me with that:
Duplicate entry '11-312' for key 'UNIQUE'
I don't know why. I ain't changing unique keys or inserting another new record. What is the problem?
Thanks.
I don't know why, when I export whole database, instead of show before SQL, show this:
CREATE TABLE IF NOT EXISTS `activity` (
`ID` int(8) NOT NULL AUTO_INCREMENT,
`action` tinyint(1) NOT NULL,
`userID` int(8) NOT NULL,
`categoryID` int(8) NOT NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `UNIQUE` (`action`,`userID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=2 ;
Now I rewrite setup table sql and it works.

MySQL Foreign Key Error

I want to create a database and connect it with two database. But I don't know when i create the Foreign Key to the second database (time_shift table) it always result error 150;
Here is the structure of table outlet:
And this is structure of table time_shift:
And this the query to create new table tide_cart:
create table `tide_chart` (
`id` int(10) not null auto_increment primary key,
`date` date null,
`outletId` int(11) not null,
`timeShiftId` int(11) not null,
`value` varchar(255) not null,
unique (`date`, `outletId`, `timeShiftId`),
foreign key (`outletId`) references `outlet`(`id`)
ON update cascade ON delete cascade,
foreign key (`timeShiftId`) references `time_shift`(`id`)
ON update cascade ON delete cascade
) engine=innoDB;
Please explain to me, why i get error when try to make foreign key connect to table time_shift?
Update: add dump export structure table for outlet and time_shift:
CREATE TABLE `outlet` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ;
CREATE TABLE `time_shift` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`time_start` time NOT NULL,
`time_end` time NOT NULL,
`is_active` tinyint(4) NOT NULL DEFAULT '1',
`ref_area` int(11) NOT NULL DEFAULT '1',
PRIMARY KEY (`id`),
KEY `ref_area` (`ref_area`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=7 ;
You need to use the InnoDB engine for your tables. time_shift is using MyISAM.
You must define indexes for outletId and timeShiftId (either UNIQUE or KEY as needed) in order to be able to create a foreign key using that field.

Nested "select ... in" performance is slow - how to fix?

Here I have a simple join query. If first two queries get results, the whole query can be done in 0.3 secs, but if the first 2 select doesn't fetch any result, the whole query will cost more than half a minute. What causes this difference? How to fix this problem and improve the performance?
SELECT * FROM music WHERE id IN
(
SELECT id FROM music_tag_map WHERE tag_id IN
(
SELECT id FROM tag WHERE content ='xxx'
)
)
LIMIT 10
Here's the table structure:
CREATE TABLE `tag` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`content` varchar(20) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `index2` (`content`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `music` (
`id` int(7) NOT NULL AUTO_INCREMENT,
`name` varchar(500) NOT NULL,
`othername` varchar(200) DEFAULT NULL,
`player` varchar(3000) DEFAULT NULL,
`genre` varchar(100) DEFAULT NULL,
`sounds` text,
`create_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `player` (`player`(255)),
KEY `name` (`othername`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `music_tag_map` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`music_id` int(7) NOT NULL,
`tag_id` int(7) NOT NULL,
`times` int(11) DEFAULT '1',
PRIMARY KEY (`id`),
KEY `music_id` (`music_id`),
KEY `tag_id` (`tag_id`),
CONSTRAINT `music_tag_map_ibfk_1` FOREIGN KEY (`id`) REFERENCES `music` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `music_tag_map_ibfk_2` FOREIGN KEY (`tag_id`) REFERENCES `tag` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
There are no joins in that query; there are two sub-selects.
A joined query would be:
SELECT *
FROM music
JOIN music_tag_map ON music.id=music_tag_map.id
JOIN tag ON music_tag_map.tag_id=tag.id
WHERE tag.content = ?
LIMIT 10;
An EXPLAIN applied to each will show you why the join performs better than the sub-select: the sub-select will scan the entire music table (the primary query), while the optimizer can pick the order of tables to scan for the joins, allowing MySQL to use indices to get only the needed rows from all the tables.