Using EXPLAIN
reveals that the following query does not use my index, could somebody please explain what is going on?
SELECT u.id AS userId, firstName, profilePhotoId, preferredActivityId, preferredSubActivityId, availabilityType,
3959 * ACOS(COS(radians(requestingUserLat)) * COS(radians(u.latitude)) * COS(radians(u.longitude) - radians(requestingUserLon)) + SIN(radians(requestingUserLat)) * SIN(radians(u.latitude))) AS distanceInMiles
FROM users u
WHERE u.latitude between lat1 and lat2 -- MySQL 5.7 supports Point data type, but it is not indexed in innoDB. I store latitude and longitude as DOUBLE for now
AND u.longitude between lon1 and lon2
AND u.dateOfBirth between maxAge and minAge -- dates are in millis, therefore maxAge will have a smaller value than minAge and so it needs to go first
AND IF(gender is null, TRUE, u.gender = gender)
AND IF(activityType is null, TRUE, u.preferredActivityType = activityType)
AND u.accountState = 'A'
AND u.id != userId
HAVING distanceInMiles < searchRadius ORDER BY distanceInMiles LIMIT pagingStart, pagingLength;
CREATE INDEX `findMatches` ON `users` (`latitude` ASC, `longitude` ASC, `dateOfBirth` ASC) USING BTREE;
The index is not used at all at this stage. To get it to work, I need to comment out a bunch of columns from the SELECT
statement, and also removed any unindexed columns from the WHERE
clause. The following works:
SELECT u.id AS userId --, firstName, profilePhotoId, preferredActivityId, preferredSubActivityId, availabilityType,
3959 * ACOS(COS(radians(requestingUserLat)) * COS(radians(u.latitude)) * COS(radians(u.longitude) - radians(requestingUserLon)) + SIN(radians(requestingUserLat)) * SIN(radians(u.latitude))) AS distanceInMiles
FROM users u
WHERE u.latitude between lat1 and lat2 -- MySQL 5.7 supports Point data type, but it is not indexed in innoDB. We store latitude and longitude as DOUBLE for now
AND u.longitude between lon1 and lon2
AND u.dateOfBirth between maxAge and minAge -- dates are in millis, therefore maxAge will have a smaller value than minAge and so it needs to go first
-- AND IF(gender is null, TRUE, u.gender = gender)
-- AND IF(activityType is null, TRUE, u.preferredActivityType = activityType)
-- AND u.accountState = 'A'
-- AND u.id != userId
HAVING distanceInMiles < searchRadius ORDER BY distanceInMiles LIMIT pagingStart, pagingLength;
Other things I tried:
I tried creating 3 distinct single-part indexes, in addition to my multi-part index that contains all 3 keys. Based on the docs here, shouldn't the optimizer merge them by creating a UNION of their qualifying rows, further speeding up execution? It's not doing it, it still selects the multi-part (covering) index.
Any help greatly appreciated!