I have the following SQL statement that is VERY slow. It varies from 600-800ms!

I'm looking for possible ways to optimize it, but not sure exactly the best route. My database is fairly big, with the entries table having 400,000 rows and the devices table having 90,000 rows.


SQL Statement

SELECT devices.manufacturer, COUNT(devices.manufacturer) AS device_count 
FROM entries 
   JOIN devices ON entries.device_id=devices.id 
WHERE waypoint_id IN (1,2,3,5) 
  AND entries.updated_at >= '2013-06-20 21:01:40 -0400' 
  AND entries.updated_at <= '2013-06-27 21:01:40 -0400'
  GROUP BY devices.manufacturer;

Is this SQL statement slow because I'm running it on poor hardware, or because the statement is bad, or have I not structured the table correctly? Any thoughts would be appreciated!


Goal of Statement

Get a list of all the device manufacturers, and the associated count of how many times that manufacturer showed up in the entries table.


Table Structure

Devices

id int(11) NOT NULL AUTO_INCREMENT,
mac_address varchar(255) DEFAULT NULL,
user_id int(11) DEFAULT NULL,
created_at datetime NOT NULL,
updated_at datetime NOT NULL,
manufacturer varchar(255) DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY mac_address (mac_address),
KEY manufacturer (manufacturer)
ENGINE=InnoDB AUTO_INCREMENT=839310 DEFAULT CHARSET=utf8;

Entries

id int(11) NOT NULL AUTO_INCREMENT,
device_id int(11) DEFAULT NULL,
created_at datetime NOT NULL,
updated_at datetime NOT NULL,
waypoint_id int(11) DEFAULT NULL,
unsure tinyint(1) DEFAULT '0',
PRIMARY KEY (id),
KEY device_index (device_id)
ENGINE=InnoDB AUTO_INCREMENT=3389538 DEFAULT CHARSET=utf8;

Also– I have been looking into alternate databases. Considering this database is going to need very fast read/writes in the future, would something like Redis be of use?

有帮助吗?

解决方案

The query would run faster if you added a multiple-column index on entries(waypoint_id, updated_at).

Also, you query would look better like this:

SELECT
    devices.manufacturer,
    COUNT(devices.manufacturer) AS device_count 
FROM
    entries
JOIN
    devices ON devices.id = entries.device_id
WHERE
    entries.waypoint_id IN (1,2,3,5)
AND
    entries.updated_at BETWEEN '2013-06-20 21:01:40 -0400' AND '2013-06-27 21:01:40 -0400'
GROUP BY
    devices.device_id

P.S.: wouldn't it be a good thing to explicitely declare device_id as a foreign key?

其他提示

You'll need an index on Entries {waypoint_id, updated_at}. This should satisfy the:

WHERE waypoint_id IN (1,2,3,5) 
  AND entries.updated_at >= '2013-06-20 21:01:40 -0400' 
  AND entries.updated_at <= '2013-06-27 21:01:40 -0400';

Depending on actual cardinalities, you may or may not want to reverse the order of fields in this composite index.

Alternatively, create a covering index on Entries {waypoint_id, updated_at, device_id}, to avoid accessing the Entries table altogether.


On top of that, consider creating an index on Devices {id, manufacturer}. Hopefully, MySQL will be smart enough to use it to satisfy both JOIN and aggregation without even accessing the Devices table.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top