我遇到了性能问题,因此以下操作在大约2.5秒内执行,只返回40行:
SELECT DISTINCT song.song_id, song.title, song.length, song.bpm, song.keysig
FROM song
INNER JOIN (
SELECT song_id
FROM song_genre
WHERE genre_id IN ('25')
) genre1 ON genre1.song_id = song.song_id
INNER JOIN (
SELECT song_id
FROM song_production
WHERE production_id IN ('8')
) production1 ON production1.song_id = song.song_id
WHERE approved='1'
ORDER by song.priority DESC, song.song_id DESC
LIMIT 0, 40
运行查询,丢弃ORDER BY
,执行时间为0.01秒等。
我理解这个问题可能与信息的计数方式有关,因为我正在使用JOINS,所以可能需要嵌套查询,但我不是100%我会怎么做呢? / p>
id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY <derived3> ALL NULL NULL NULL NULL 321 Using temporary; Using filesort 1 PRIMARY <derived2> ALL NULL NULL NULL NULL 3424 Using join buffer 1 PRIMARY song eq_ref PRIMARY PRIMARY 4 production1.song_id 1 Using where 3 DERIVED song_production ref PRIMARY PRIMARY 4 339 Using index 2 DERIVED song_genre index NULL PRIMARY 8 NULL 3424 Using where; Using index
表song
:
CREATE TABLE `song` (
`song_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` tinytext,
`length` varchar(5) DEFAULT NULL,
`Type` varchar(1) DEFAULT NULL,
`Vocals` varchar(10) DEFAULT NULL,
`Themes` varchar(10) DEFAULT NULL,
`Explicit` varchar(10) DEFAULT NULL,
`timesig` varchar(3) DEFAULT NULL,
`keysig` varchar(250) NOT NULL,
`bpm` int(3) DEFAULT NULL,
`speed` varchar(7) DEFAULT NULL,
`Era` varchar(10) DEFAULT NULL,
`Language` varchar(10) DEFAULT NULL,
`Keywords` varchar(10) DEFAULT NULL,
`description` mediumtext,
`search_description` longtext NOT NULL,
`key` varchar(25) NOT NULL,
`priority` int(2) NOT NULL,
`approved` int(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`song_id`),
FULLTEXT KEY `description` (`description`),
FULLTEXT KEY `search_description` (`search_description`),
FULLTEXT KEY `title` (`title`),
FULLTEXT KEY `head_desc` (`title`,`search_description`)
) ENGINE=MyISAM
AUTO_INCREMENT=1388
DEFAULT CHARSET=utf8 ;
表song_genre
:
CREATE TABLE `song_genre` (
`genre_id` int(10) NOT NULL,
`song_id` int(10) NOT NULL,
PRIMARY KEY (`genre_id`,`song_id`)
) ENGINE=MyISAM
DEFAULT CHARSET=latin1 ;
表song_production
:
CREATE TABLE `song_production` (
`production_id` int(10) NOT NULL,
`song_id` int(10) NOT NULL,
PRIMARY KEY (`production_id`,`song_id`)
) ENGINE=MyISAM
DEFAULT CHARSET=latin1 ;
答案 0 :(得分:2)
第一步:
我认为genre_id
,production_id
和approved
是integer
列,而不是char
,所以在{{{{{{ 1}}。取消引用它们:'25'
如果删除25
会怎样? (没有表的结构,主要,外键和唯一约束,我们无法知道)。你在结果中得到多个相同的行吗?如果不是,请删除DISTINCT
。如果是,请将其删除并添加DISTINCT
:
GROUP BY song.song_id
第二步:
添加有用的索引。 SELECT song.song_id, song.title, song.length, song.bpm, song.keysig
FROM song
INNER JOIN (
SELECT song_id
FROM song_genre
WHERE genre_id IN (25)
) genre1 ON genre1.song_id = song.song_id
INNER JOIN (
SELECT song_id
FROM song_production
WHERE production_id IN (8)
) production1 ON production1.song_id = song.song_id
WHERE approved = 1
----- GROUP BY song.song_id --- not needed at all (with these tables)
--- (and structure)
ORDER BY song.priority DESC, song.song_id DESC
LIMIT 0, 40 ;
上的索引可能有助于查询。
您(approved, priority, song_id)
列定义为song.song_id
,而UNSIGNED INT
和song_genre.song_id
定义为song_production.song_id
。如果您将它们转换为SIGNED INT
也会很好。
我还会在UNSIGNED INT
和(song_id, genre_id)
上添加(唯一)索引。它们可能对此查询没有用,但在其他情况下你肯定需要这些索引。
第三步:
尝试以其他方式重写查询。没有派生表,例如:
(song_id, production_id)
或SELECT song.song_id, song.title, song.length, song.bpm, song.keysig
FROM song
INNER JOIN
song_genre AS genre1
ON genre1.song_id = song.song_id
INNER JOIN
song_production AS production1
ON production1.song_id = song.song_id
WHERE song.approved = 1
AND genre1.genre_id IN (25)
AND production1.production_id IN (8)
ORDER BY song.priority DESC
, song.song_id DESC
LIMIT 0, 40 ;
:
EXISTS
并测试哪一个表现得更快。
答案 1 :(得分:0)
MySQL会在应用限制之前对所有行运行ORDER BY
,因此如果您的song
表很大,并且没有正确编入索引,那么它仍然会很慢。您可以使用post on MySQL Performance Blog一些方法来加速ORDER BY ... LIMIT
次查询。
我会注意到子查询有点多余,连接会处理这个问题。你可以像这样重写这个查询:
SELECT DISTINCT song.song_id, song.title, song.length, song.bpm, song.keysig
FROM song
JOIN song_genre g
ON g.song_id = song.song_id
JOIN song_production p
ON p.song_id = song.song_id
WHERE approved='1'
AND g.genre_id IN ('25')
AND p.production_id IN ('8')
ORDER by priority DESC, song_id DESC
LIMIT 0, 40
我甚至不确定你是否需要SELECT上的DISTINCT
,除非你有多首歌曲具有完全相同的id / title / length / bpm / keysig值。
答案 2 :(得分:-1)
好的,我只是重写了查询:
SELECT DISTINCT song.song_id, song.title, song.length, song.bpm, song.keysig
FROM song
INNER JOIN (
SELECT song_id
FROM song_genre
WHERE genre_id LIKE '%'
) genre1
ON genre1.song_id = song.song_id
INNER JOIN (
SELECT song_id
FROM song_production
WHERE production_id IN ('5')
) production1
ON production1.song_id = song.song_id
WHERE approved='1'
ORDER by song.priority DESC, song.song_id DESC
LIMIT 0, 40
首先,我在桌面歌曲上应用顺序,歌曲在索引中有song_id。我也希望优先考虑。如果没有,请在此处添加索引,并且order by不会通过临时表和filesort传递。
如果您没有为订单提供特定的表,MySQL可以选择错误的表来执行订单。