2

I'm running a query that touches 2 moderately sized tables (1-5 million rows). EXPLAIN shows it touches only indexes and very few rows, however it takes very long to complete (~8 seconds). The only thing that might be of concern is the temporary table it creates. If anyone has any ideas, it would be appreciated!

Query:

SELECT `visits`.`id` 
FROM `visits` 
INNER JOIN `visits_views` ON visits_views.visit_id=visits.id
GROUP BY `visits`.`id` 
ORDER BY `visits`.`created` DESC 
LIMIT 20

EXPLAIN:

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE visits index PRIMARY created 4 NULL 20 Using index; Using temporary
1 SIMPLE visits_views ref visit_id visit_id 4 visits.id 1 Using index

Table structure

CREATE TABLE `visits` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `affiliate_id` int(11) DEFAULT NULL,
 `referer` varchar(250) DEFAULT NULL,
 `domain` varchar(70) DEFAULT NULL,
 `user_agent` varchar(300) DEFAULT NULL,
 `country` char(2) DEFAULT NULL,
 `ip` int(10) unsigned DEFAULT NULL,
 `bot` tinyint(1) NOT NULL DEFAULT '0',
 `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
 PRIMARY KEY (`id`),
 KEY `domain` (`domain`),
 KEY `created` (`created`),
 KEY `referer` (`referer`),
 KEY `ip` (`ip`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `visits_views` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `visit_id` int(11) NOT NULL,
 `url` varchar(250) NOT NULL,
 `prev_id` int(11) DEFAULT NULL,
 `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
 PRIMARY KEY (`id`),
 KEY `visit_id` (`visit_id`),
 KEY `created` (`created`),
 KEY `url` (`url`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
asked Jan 6, 2015 at 21:18

1 Answer 1

3

In order to make very small temp tables, you need to rewrite the query to do this:

  • LIMIT first on the 20 most recent created timestamps
  • then JOIN those 20 ids to visits_views

Here is the proposed query

SELECT A.id FROM
(SELECT id FROM visits ORDER BY created DESC LIMIT 20) A
LEFT JOIN `visits_views` B ON A.id = B.visit_id;

I use LEFT JOIN to preserve the order of the subquery

If you want to count the views

SELECT * FROM
(
 SELECT A.id,A.created,COUNT(1) views FROM
 (SELECT id,created FROM visits ORDER BY created DESC LIMIT 20) A
 LEFT JOIN `visits_views` B ON A.id = B.visit_id
 GROUP BY A.id,A.created
) AA ORDER BY created;

I have addressed making small temp tables in queries like this. See my post Fetching a Single Row from Join Table in StackOverflow. It demonstrates how I did a LIMIT 40 on a subquery and used that 40 row result set to JOIN to other tables.

answered Jan 6, 2015 at 21:27
1
  • It works very well, query now runs in the sub-millisecond range. I'm wondering you added the additional external SELECT clause for ordering in your last query - ordering the inner SELECT by creation date seems to work fine. Commented Jan 6, 2015 at 22:24

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.