dotslashlu
dotslashlu

Reputation: 3401

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;

Upvotes: 1

Views: 2195

Answers (1)

outis
outis

Reputation: 77450

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.

Upvotes: 2

Related Questions