Search code examples
mysqlsqlsql-optimization

Alternative to GROUP BY to optimize SQL call


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?


Solution

  • 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?