MySQL Query Using LEFT JOIN Hangs - mysql

Can somebody please advise where I could be going wrong with the following query which is causing my page to hang:
SELECT m.member_id, m.member_name, SUM(CONCAT(f.pounds, '.', f.pence)) AS totalfigure
FROM members m
LEFT JOIN figures f ON f.member_id = m.member_id AND f.period_id = 45
WHERE m.added BETWEEN '1980-01-01' AND '2014-10-31'
AND (DATE_SUB('2014-08-01', INTERVAL 60 DAY)) < m.removed
GROUP BY m.member_id
ORDER BY m.member_name;
The same query runs fine if I use JOIN rather than LEFT JOIN but I need to also view members that do not have an entry in the figures table.
This is the EXPLAIN result I get with the LEFT JOIN:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE m ALL NULL NULL NULL NULL 3128 Using temporary; Using filesort
1 SIMPLE f ALL NULL NULL NULL NULL 169214
and with the JOIN the result is:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE f ALL NULL NULL NULL NULL 169214 Using where; Using temporary; Using filesort
1 SIMPLE m eq_ref PRIMARY PRIMARY 3 db.f.member_id 1
The structure for each of these tables is:
CREATE TABLE IF NOT EXISTS `members` (
`member_id` mediumint(5) unsigned NOT NULL AUTO_INCREMENT,
`member_name` varchar(100) NOT NULL DEFAULT '',
PRIMARY KEY (`member_id`),
FULLTEXT KEY `member_name` (`member_name`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=3462 ;
CREATE TABLE IF NOT EXISTS `figures` (
`supplier_id` int(10) unsigned NOT NULL DEFAULT '0',
`contract_id` mediumint(5) unsigned NOT NULL DEFAULT '1',
`member_id` mediumint(5) unsigned NOT NULL,
`period_id` mediumint(5) unsigned NOT NULL DEFAULT '0',
`pounds` varchar(8) DEFAULT NULL,
`pence` char(3) DEFAULT '00',
PRIMARY KEY (`supplier_id`,`member_id`,`period_id`,`contract_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
Thanks

Related

Adding a LIMIT 1 to a simple MySQL Query causes it to hang

I have a strange issue where by adding LIMIT 1 to a query, it causes the query to never return. And yet, when I remove that LIMIT 1 the query works instantly. LIMIT 1 should typically speed things up, not slow them down, so I'm wondering what I'm missing here.
The query that works fine is this:
Hangs For Infinite
SELECT
`groups`.*
FROM
`groups`
INNER JOIN
`positions`
ON
`groups`.`uid` = `positions`.`group_uid`
WHERE
`positions`.`component_uid` = '1234'
AND
(`groups`.deleted_at IS NULL)
AND
(`positions`.deleted_at IS NULL)
LIMIT
1
Associated EXPLAIN Result:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE groups ALL index_groups_on_deleted_at NULL NULL NULL 6832 Using where
1 SIMPLE positions ref index_positions_on_deleted_at index_positions_on_deleted_at 6 const 22110 Using index condition; Using where
Works Just Fine
SELECT
`groups`.*
FROM
`groups`
INNER JOIN
`positions`
ON
`groups`.`uid` = `positions`.`group_uid`
WHERE
`positions`.`component_uid` = '1234'
AND
(`groups`.deleted_at IS NULL)
AND
(`positions`.deleted_at IS NULL)
/* LIMIT
1 */
Associated EXPLAIN Result:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE groups ALL index_groups_on_deleted_at NULL NULL NULL 6832 Using where
1 SIMPLE positions ALL index_positions_on_deleted_at NULL NULL NULL 44220 Using where; Using join buffer (Block Nested Loop)
CREATE TABLE Commands:
CREATE TABLE `groups` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` varchar(255) NOT NULL,
`deleted_at` datetime DEFAULT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `index_groups_on_deleted_at` (`deleted_at`)
) ENGINE=InnoDB AUTO_INCREMENT=6941 DEFAULT CHARSET=utf8;
CREATE TABLE `positions` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` varchar(255) NOT NULL,
`component_uid` varchar(255) NOT NULL,
`deleted_at` datetime DEFAULT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
`routing_group_uid` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_positions_on_deleted_at` (`deleted_at`)
) ENGINE=InnoDB AUTO_INCREMENT=44321 DEFAULT CHARSET=utf8;
But there's no errors produced so I can't figure out what could be going wrong.
Any ideas on what the issue could be would be great.
Thanks.
Update
From comments that:
Mysql documentation mentions the use of IS NULL for optimization that it can handles only one.
This still hangs:
SELECT
`groups`.*
FROM
`groups`
INNER JOIN
`positions`
ON
`groups`.`uid` = `positions`.`group_uid`
WHERE
`positions`.`component_uid` = '1234'
/* AND
(`groups`.deleted_at IS NULL) */
AND
(`positions`.deleted_at IS NULL)
LIMIT
1
But this works fine:
SELECT
`groups`.*
FROM
`groups`
INNER JOIN
`positions`
ON
`groups`.`uid` = `positions`.`group_uid`
WHERE
`positions`.`component_uid` = '1234'
AND
(`groups`.deleted_at IS NULL)
/* AND
(`positions`.deleted_at IS NULL) */
LIMIT
1

Slow query with joined derived tables

I have a few queries on a "custom dashboard" of my application, and one of them is taking 10-12 seconds to execute. Using EXPLAIN I can see why it's slow, but I don't know what to do about it. Here is the query:
SELECT person.PersonID,FullName,Furigana,qualdate FROM person
INNER JOIN (
SELECT pq.PersonID,MAX(ContactDate) AS qualdate FROM person pq
INNER JOIN contact cq ON pq.PersonID=cq.PersonID
WHERE cq.ContactTypeID IN (22,26,45) GROUP BY pq.PersonID
) qual ON person.PersonID=qual.PersonID
LEFT OUTER JOIN (
SELECT pe.personID,MAX(ContactDate) AS elimdate FROM person pe
INNER JOIN contact ce ON pe.PersonID=ce.PersonID WHERE ce.ContactTypeID IN (25,31,30,41,23,42,2,33,35,29,12)
GROUP BY pe.PersonID
) elim ON qual.PersonID=elim.PersonID
LEFT OUTER JOIN (
SELECT po.personID FROM person po
INNER JOIN percat pc ON po.PersonID=pc.PersonID WHERE pc.CategoryID=38
) overseas ON qual.PersonID=overseas.PersonID
WHERE (elimdate IS NULL OR qualdate > elimdate)
AND qualdate < CURDATE()-INTERVAL 7 DAY
AND overseas.PersonID IS NULL
ORDER BY qualdate
And here is the EXPLAIN result:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 5447 Using where; Using temporary; Using filesort
1 PRIMARY <derived3> ALL NULL NULL NULL NULL 5565 Using where
1 PRIMARY <derived4> ALL NULL NULL NULL NULL 9 Using where; Not exists
1 PRIMARY person eq_ref PRIMARY PRIMARY 4 qual.PersonID 1
4 DERIVED pc ref PRIMARY,CategoryID CategoryID 4 8
4 DERIVED po eq_ref PRIMARY PRIMARY 4 kizuna_misa.pc.PersonID 1 Using index
3 DERIVED pe index PRIMARY PRIMARY 4 NULL 5964 Using index
3 DERIVED ce ref PersonID,ContactTypeID PersonID 4 kizuna_misa.pe.PersonID 1 Using where
2 DERIVED pq index PRIMARY PRIMARY 4 NULL 5964 Using index
2 DERIVED cq ref PersonID,ContactTypeID PersonID 4 kizuna_misa.pq.PersonID 1 Using where
I'm sure the first line of the EXPLAIN reveals the problem (comparing with similar queries, it appears that the second line isn't too slow), but I don't know how to fix it. I already have indexes on every column that appears in the joins, but since the tables are <derived2> etc., I guess indexes are irrelevant.
The objective (since it's probably not obvious to someone unfamiliar with my application and schema) is a followup tickler list - if one of the #22/26/45 contacts has occurred but nothing has been done in response (either one of several other contacts or designating by a category assignment that the person is overseas), then the person should appear in the list for followup after waiting a week. Subqueries are easier for me to write and understand than these messy joins, but I can't check the sequence of dates (and subqueries are often slow, also).
EDIT (in response to Rick James):
MySQL version is 5.0.95 (yeah, I know...). And here is SHOW CREATE TABLE for the three tables involved, even though most of the fields in person are irrelevant:
CREATE TABLE `contact` (
`ContactID` int(11) unsigned NOT NULL auto_increment,
`PersonID` int(11) unsigned NOT NULL default '0',
`ContactTypeID` int(11) unsigned NOT NULL default '0',
`ContactDate` date NOT NULL default '0000-00-00',
`Description` text,
PRIMARY KEY (`ContactID`),
KEY `ContactDate` (`ContactDate`),
KEY `PersonID` (`PersonID`),
KEY `ContactTypeID` (`ContactTypeID`)
) ENGINE=MyISAM AUTO_INCREMENT=16901 DEFAULT CHARSET=utf8
CREATE TABLE `percat` (
`PersonID` int(11) unsigned NOT NULL default '0',
`CategoryID` int(11) unsigned NOT NULL default '0',
PRIMARY KEY (`PersonID`,`CategoryID`),
KEY `CategoryID` (`CategoryID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
CREATE TABLE `person` (
`PersonID` int(11) unsigned NOT NULL auto_increment,
`FullName` varchar(100) NOT NULL default '',
`Furigana` varchar(100) NOT NULL default '',
`Sex` enum('','M','F') character set ascii NOT NULL default '',
`HouseholdID` int(11) unsigned NOT NULL default '0',
`Relation` varchar(6) character set ascii NOT NULL default '',
`Title` varchar(6) NOT NULL default '',
`CellPhone` varchar(30) character set ascii NOT NULL default '',
`Email` varchar(70) character set ascii NOT NULL default '',
`Birthdate` date NOT NULL default '0000-00-00',
`Country` varchar(30) NOT NULL default '',
`URL` varchar(150) NOT NULL default '',
`Organization` tinyint(1) NOT NULL default '0',
`Remarks` text NOT NULL,
`Photo` tinyint(1) NOT NULL default '0',
`UpdDate` date NOT NULL default '0000-00-00',
PRIMARY KEY (`PersonID`),
KEY `Furigana` (`Furigana`),
KEY `FullName` (`FullName`),
KEY `Email` (`Email`),
KEY `Organization` (`Organization`,`Furigana`)
) ENGINE=MyISAM AUTO_INCREMENT=6063 DEFAULT CHARSET=utf8
Attempted suggestion:
I tried to implement Rick James's suggestion of putting the subselects in the field list (I didn't even know that was possible), like this:
SELECT
p.PersonID,
FullName,
Furigana,
(SELECT MAX(ContactDate) FROM contact cq
WHERE cq.PersonID=p.PersonID
AND cq.ContactTypeID IN (22,26,45))
AS qualdate,
(SELECT MAX(ContactDate) FROM contact ce
WHERE ce.PersonID=p.PersonID
AND ce.ContactTypeID IN (25,31,30,41,23,42,2,33,35,29,12))
AS elimdate
FROM person p
WHERE (elimdate IS NULL OR qualdate > elimdate)
AND qualdate < CURDATE()-INTERVAL 7 DAY
AND NOT EXISTS (SELECT * FROM percat WHERE CategoryID=38 AND percat.PersonID=p.PersonID)
ORDER BY qualdate
But it complains: #1054 - Unknown column 'elimdate' in 'where clause' According to the docs, WHERE clauses are interpreted before field lists, so this approach isn't going to work.
You have an interesting query. I am not sure what the best solution is. Here are two guesses:
Plan A
INDEX(qualdate)
may help. Please provide SHOW CREATE TABLE.
This construct optimizes poorly:
FROM ( SELECT ... )
JOIN ( SELECT ... )
In your case, overseas should probably turned into a JOIN, not a subselect. And the other two should probably be turned into a different flavor of dependent subquery:
SELECT ...,
( SELECT MAX(...) ... ) AS qualdate,
( SELECT MAX(...) ... ) AS elimdate
FROM ...
What version of MySQL are you running?
Plan B
If practical, fold these into the subqueries so that they generate fewer rows, thereby leading to less effort at the outer query. (One per subquery)
elimdate IS NOT NULL
qualdate < CURDATE()-INTERVAL 7 DAY
overseas.PersonID IS NOT NULL
Perhaps the NULL tests apply to LEFT and this suggestion may not apply.

Hint indexes to mysql on Join

EXPLAIN SELECT SENDER AS PROFILEID, MESSAGE, 'R' AS SR, SEEN, DATE
FROM `MESSAGE_LOG`
JOIN MESSAGES ON ( MESSAGE_LOG.ID = MESSAGES.ID )
WHERE `RECEIVER` = '9063911' AND SENDER NOT
IN ('658', '87238', '99359', '643848', '651922', '734783', '880643'
) AND `TYPE` = 'R' AND `IS_MSG` = 'Y'
UNION SELECT RECEIVER AS PROFILEID, MESSAGE, 'S' AS SR, SEEN, DATE
FROM `MESSAGE_LOG`
JOIN MESSAGES ON ( MESSAGE_LOG.ID = MESSAGES.ID )
WHERE `SENDER` = '9063911' AND RECEIVER NOT
IN (
'658', '87238', '99359', '643848', '651922', '734783', '880643'
) AND `TYPE` = 'R' AND `IS_MSG` = 'Y'
ORDER BY DATE DESC
Explain query returns:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY MESSAGE_LOG ref ID,SENDER,RECEIVER RECEIVER 4 const 142 Using where
1 PRIMARY MESSAGES eq_ref ID ID 4 newjs.MESSAGE_LOG.ID 1
2 UNION MESSAGE_LOG range ID,SENDER,RECEIVER SENDER 8 NULL 168 Using where
2 UNION MESSAGES eq_ref ID ID 4 newjs.MESSAGE_LOG.ID 1
NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL
The query is taking around 15 sec to execute. What could be the reason and how the query can be optimized further.
EDIT: Is it because mysql is not able to utilize indexes properly?
Indexes on MESSAGE_LOG table:
Keyname Type Cardinality Action Field
ID UNIQUE 44491833 Edit Drop ID
SENDER INDEX 44491833 Edit Drop SENDER,RECEIVER
RECEIVER INDEX 1483061 Edit Drop RECEIVER,FOLDERID,OBSCENE
Indexes on MESSAGES table:
Keyname Type Cardinality Action Field
ID UNIQUE 43572638 Edit Drop ID
Can I hint mysql to use right index? If yes, then how and on which column?
Tables structure:
CREATE TABLE `MESSAGE_LOG` (
`SENDER` int(8) unsigned NOT NULL DEFAULT '0',
`RECEIVER` int(8) unsigned NOT NULL DEFAULT '0',
`DATE` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`IP` int(10) unsigned DEFAULT NULL,
`RECEIVER_STATUS` char(1) NOT NULL DEFAULT 'U',
`FOLDERID` mediumint(9) NOT NULL DEFAULT '0',
`MSG_OBS_ID` int(11) NOT NULL DEFAULT '0',
`SENDER_STATUS` char(1) NOT NULL DEFAULT 'U',
`TYPE` char(1) NOT NULL DEFAULT 'R',
`ID` int(11) NOT NULL,
`OBSCENE` char(1) NOT NULL DEFAULT 'N',
`IS_MSG` char(1) NOT NULL DEFAULT 'N',
`SEEN` char(1) NOT NULL,
UNIQUE KEY `ID` (`ID`),
KEY `SENDER` (`SENDER`,`RECEIVER`),
KEY `RECEIVER` (`RECEIVER`,`FOLDERID`,`OBSCENE`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
CREATE TABLE `MESSAGES` (
`ID` int(11) NOT NULL,
`MESSAGE` text NOT NULL,
UNIQUE KEY `ID` (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

Avoid filesort with INNER JOIN + ORDER BY

I've been reading other posts but I didn't managed to fix my query.
Using DESC order the query is x20 times slower, I must improve that.
This is the query:
SELECT posts.post_id, posts.post_b_id, posts.post_title, posts.post_cont, posts.thumb, posts.post_user, boards.board_title_l, boards.board_title
FROM posts
INNER JOIN follow ON posts.post_b_id = follow.board_id
INNER JOIN boards ON posts.post_b_id = boards.board_id
WHERE follow.user_id =1
ORDER BY posts.post_id DESC
LIMIT 10
And these are the tables (Updated):
CREATE TABLE IF NOT EXISTS `posts` (
`post_id` int(11) NOT NULL AUTO_INCREMENT,
`post_b_id` int(11) unsigned NOT NULL,
`post_title` varchar(50) COLLATE utf8_bin NOT NULL,
`post_cont` text COLLATE utf8_bin NOT NULL,
`post_mintxt` varchar(255) COLLATE utf8_bin NOT NULL,
`post_type` char(3) COLLATE utf8_bin NOT NULL,
`thumb` varchar(200) COLLATE utf8_bin NOT NULL,
`post_user` varchar(16) COLLATE utf8_bin NOT NULL,
`published` enum('0','1') COLLATE utf8_bin NOT NULL,
`post_ip` varchar(94) COLLATE utf8_bin NOT NULL,
`post_ip_dat` int(11) unsigned NOT NULL,
`post_up` int(10) unsigned NOT NULL DEFAULT '0',
`post_down` int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`post_id`),
KEY `post_b_id` (`post_b_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=405 ;
CREATE TABLE IF NOT EXISTS `boards` (
`board_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`board_title_l` varchar(19) COLLATE utf8_bin NOT NULL,
`board_user_id` int(10) unsigned NOT NULL,
`board_title` varchar(19) COLLATE utf8_bin NOT NULL,
`board_user` varchar(16) COLLATE utf8_bin NOT NULL,
`board_txt` tinyint(1) unsigned NOT NULL,
`board_img` tinyint(1) unsigned NOT NULL,
`board_vid` tinyint(1) unsigned NOT NULL,
`board_desc` varchar(100) COLLATE utf8_bin NOT NULL,
`board_mod_p` tinyint(3) unsigned NOT NULL DEFAULT '0',
`board_ip` varchar(94) COLLATE utf8_bin NOT NULL,
`board_dat_ip` int(11) unsigned NOT NULL,
PRIMARY KEY (`board_id`),
UNIQUE KEY `board_title_l` (`board_title_l`),
KEY `board_user_id` (`board_user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=89 ;
CREATE TABLE IF NOT EXISTS `follow` (
`user_id` int(10) unsigned NOT NULL,
`board_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`user_id`,`board_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
Using default ASC order it only uses index and where, with DESC uses index, where, temporary and filesort.
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE follow ref user_id user_id 4 const 2 100.00 Using index; Using temporary; Using filesort
1 SIMPLE boards eq_ref PRIMARY PRIMARY 4 xxxx.follow.board_id 1 100.00
1 SIMPLE posts ref post_b_id post_b_id 4 xxxx.boards.board_id 3 100.00 Using where
How I can make the query receiving the results in DESC order without filesort and temporary.
UPDATE: I made a new query, no temporary or filesort, but type: index, filtered: 7340.00. Almost as fast as ASC order if the posts are at the end of the table, but slow if the posts that is searching are at the beginning. So seems better but it's not enough.
SELECT posts.post_id, posts.post_b_id, posts.post_title, posts.post_cont, posts.thumb, posts.post_user, boards.board_title_l, boards.board_title
FROM posts INNER JOIN boards ON posts.post_b_id = boards.board_id
WHERE posts.post_b_id
IN (
SELECT follow.board_id
FROM follow
WHERE follow.user_id = 1
)
ORDER BY posts.post_id DESC
LIMIT 10
Explain:
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY posts index post_b_id PRIMARY 8 NULL 10 7340.00 Using where
1 PRIMARY boards eq_ref PRIMARY PRIMARY 4 xxxx.posts.post_b_id 1 100.00
2 DEPENDENT SUBQUERY follow eq_ref user_id user_id 8 const,func 1 100.00 Using index
UPDATE: Explain for the query from dened's answer:
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY <derived2>ALL NULL NULL NULL NULL 10 100.00
1 PRIMARY posts eq_ref PRIMARY,post_b_id PRIMARY 4 sq.post_id 1 100.00
1 PRIMARY boards eq_ref PRIMARY PRIMARY 4 xxxx.posts.post_b_id 1 100.00
2 DERIVED follow ref PRIMARY PRIMARY 4 1 100.00 Using index; Using temporary; Using filesort
2 DERIVED posts ref post_b_id post_b_id 4 xxxx.follow.board_id 6 100.00 Using index
Times:
Original query no order (ASC): 0.187500 seconds
Original query DESC: 2.812500 seconds
Second query posts at the end (DESC): 0.218750 seconds
Second query posts at the beginning (DESC): 3.293750 seconds
dened's query DESC: 0.421875 seconds
dened's query no order (ASC): 0.323750 seconds
Interesting note, if I add ORDER BY ASC is as slow as DESC.
Alter the table order will be a god way, but as I said in the comments I wasn't able to do that.
You can help MySQL optimizer by moving all the filtering work to a subquery that accesses only indices (manipulating indices is usually much faster than manipulating other data), and fetching rest of the data in the outermost query:
SELECT posts.post_id,
posts.post_b_id,
posts.post_title,
posts.post_cont,
posts.thumb,
posts.post_user,
boards.board_title_l,
boards.board_title
FROM (SELECT post_id
FROM posts
JOIN follow
ON posts.post_b_id = follow.board_id
WHERE follow.user_id = 1
ORDER BY post_id DESC
LIMIT 10) sq
JOIN posts
ON posts.post_id = sq.post_id
JOIN boards
ON boards.board_id = posts.post_b_id
Note that I omit ORDER BY posts.post_id DESC from the outer query, because it is usually faster to sort the final result in your code rather than sorting using a MySQL query (MySQL often uses filesort for that).
P.S. You can replace the unique key in the follow table with a primary key.
Increasing the sort_buffer_size parameter will increase the amount of memory MySQL uses before resorting to a temporary disk file and should help considerably.

MySQL stuck on "using filesort" when doing an "order by"

I can't seem to get my query to stop using filesort.
This is my query:
SELECT s.`pilot`, p.`name`, s.`sector`, s.`hull`
FROM `pilots` p
LEFT JOIN `ships` s ON ( (s.`game` = p.`game`)
AND (s.`pilot` = p.`id`) )
WHERE p.`game` = 1
AND p.`id` <> 2
AND s.`sector` = 43
AND s.`hull` > 0
ORDER BY p.`last_move` DESC
Table structures:
CREATE TABLE IF NOT EXISTS `pilots` (
`id` mediumint(5) unsigned NOT NULL AUTO_INCREMENT,
`game` tinyint(3) unsigned NOT NULL DEFAULT '0',
`last_move` int(10) NOT NULL DEFAULT '0',
UNIQUE KEY `id` (`id`),
KEY `last_move` (`last_move`),
KEY `game_id_lastmove` (`game`,`id`,`last_move`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=8 ;
CREATE TABLE IF NOT EXISTS `ships` (
`id` mediumint(5) unsigned NOT NULL AUTO_INCREMENT,
`game` tinyint(3) unsigned NOT NULL DEFAULT '0',
`pilot` mediumint(5) unsigned NOT NULL DEFAULT '0',
`sector` smallint(5) unsigned NOT NULL DEFAULT '0',
`hull` smallint(4) unsigned NOT NULL DEFAULT '50',
UNIQUE KEY `id` (`id`),
KEY `game` (`game`),
KEY `pilot` (`pilot`),
KEY `sector` (`sector`),
KEY `hull` (`hull`),
KEY `game_2` (`game`,`pilot`,`sector`,`hull`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=8 ;
The explain:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE p ref id,game_id_lastmove game_id_lastmove 1 const 7 Using where; Using filesort
1 SIMPLE s ref game,pilot,sector... game_2 6 const,fightclub_alpha.p.id,const 1 Using where; Using index
edit: I cut some of the unnecessary pieces out of my queries/table structure.
Anybody have any ideas?
the best thing that you can do is to make indexes:
index that covers table ships with fields: game + pilot + sector + hull (in this specific order)
pilots: game + id
this particular query will always use filesort, because it has not range condition p.id <> 2
http://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html
In some cases, MySQL cannot use
indexes to resolve the ORDER BY,
although it still uses indexes to find
the rows that match the WHERE clause.
These cases include the following ...
The key used to fetch the rows is not
the same as the one used in the ORDER
BY