Hi,

I hope this is the right place to ask a question about query optimization.

Background:
I have a database which has events, which occur in places (listings). Places
have addresses, and addresses belong to a city. I can select the latest
event within a particular city very efficiently (less than 2ms), but
selecting the latest listing within a city is REALLY slow (10-20 seconds)
despite being almost a subset of the event query.

I have been working on this for about a day, and have tried all sorts of
tweaks to the indexes but to no avail. I always seem to end up with 'using
temporary; using filesort' as the 'extra' content in the explain result. If
anyone has a suggestion for what I might do to fix this, I'd really
appreciate it. If not, I could further de-normalize the database for
performance reasons, but I would feel dirty for doing so.

Here is the fast query (select the latest event within a particular city),
and it's explain.
SELECT
  events.*, listings.*, addresses.*
  FROM
    `events`
    LEFT OUTER JOIN
      `listings` ON `listings`.id = `events`.listing_id
    LEFT OUTER JOIN
      `addresses` ON `addresses`.addressable_id = `listings`.id
      AND
      `addresses`.addressable_type = 'Listing'
  WHERE (addresses.parent_city_id = 3)
  ORDER BY events.id DESC LIMIT 1

  
+----+-------------+-----------+--------+----------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+---------+------------------------------+------+-------------+
  | id | select_type | table     | type   | possible_keys

                           | key
       | key_len | ref                          | rows | Extra       |
  
+----+-------------+-----------+--------+----------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+---------+------------------------------+------+-------------+
  |  1 | SIMPLE      | events    | index  | index_events_on_listing_id

                          | PRIMARY
       | 4       | NULL                         |    1 |             |
  |  1 | SIMPLE      | listings  | eq_ref | PRIMARY

                           | PRIMARY
       | 4       | ratemyarea.events.listing_id |    1 | Using where |
  |  1 | SIMPLE      | addresses | ref    |
index_addresses_on_parent_city_id_and_addressable_type,index_addresses_on_addressable_type_and_addressable_id,addressable_id_type_city
| index_addresses_on_addressable_type_and_addressable_id | 773     | const,
ratemyarea.listings.id |    1 | Using where |
  
+----+-------------+-----------+--------+----------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+---------+------------------------------+------+-------------+


Here is the slow query (select the latest listing within a particular city),
and it's explain
SELECT
  listings.*, addresses.*
  FROM
    `listings`
    LEFT OUTER JOIN
      `addresses` ON `addresses`.addressable_id = `listings`.id
      AND
      `addresses`.addressable_type = 'Listing'
  WHERE (addresses.parent_city_id = 3)
  ORDER BY listings.id DESC LIMIT 1

  
+----+-------------+-----------+--------+----------------------------------------------------------------------------------------------------------------------------------------+--------------------------+---------+-------------------------------------+-------+----------------------------------------------+
  | id | select_type | table     | type   | possible_keys

                           | key                      | key_len | ref
                          | rows  | Extra
     |
  
+----+-------------+-----------+--------+----------------------------------------------------------------------------------------------------------------------------------------+--------------------------+---------+-------------------------------------+-------+----------------------------------------------+
  |  1 | SIMPLE      | addresses | ref    |
index_addresses_on_parent_city_id_and_addressable_type,index_addresses_on_addressable_type_and_addressable_id,addressable_id_type_city
| addressable_id_type_city | 773     | const,const                         |
25680 | Using where; Using temporary; Using filesort |
  |  1 | SIMPLE      | listings  | eq_ref | PRIMARY

                           | PRIMARY                  | 4       |
ratemyarea.addresses.addressable_id |     1 |
               |
  
+----+-------------+-----------+--------+----------------------------------------------------------------------------------------------------------------------------------------+--------------------------+---------+-------------------------------------+-------+----------------------------------------------+


Here is the structure of the tables:

CREATE TABLE `addresses` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `postal_code` varchar(255) DEFAULT NULL,
  `addressable_type` varchar(255) DEFAULT NULL,
  `addressable_id` int(11) DEFAULT NULL,
  `parent_city_id` int(11) DEFAULT NULL,
  `lat` decimal(15,10) DEFAULT NULL,
  `lng` decimal(15,10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_addresses_on_lat_and_lng` (`lat`,`lng`),
  KEY `index_addresses_on_parent_city_id_and_addressable_type`
(`parent_city_id`,`addressable_type`),
  KEY `index_addresses_on_addressable_type_and_addressable_id`
(`addressable_type`,`addressable_id`)
) ENGINE=InnoDB AUTO_INCREMENT=120513 DEFAULT CHARSET=utf8;



# Dump of table cities
# ------------------------------------------------------------

CREATE TABLE `cities` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `country_id` int(11) NOT NULL,
  `lat` decimal(15,10) NOT NULL,
  `lng` decimal(15,10) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `index_cities_on_name` (`name`),
  KEY `index_cities_on_country_id` (`country_id`),
  KEY `index_cities_on_lat_and_lng` (`lat`,`lng`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;



# Dump of table events
# ------------------------------------------------------------

CREATE TABLE `events` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `listing_id` int(11) NOT NULL,
  `description` text,
  `title` varchar(150) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `index_events_on_title` (`title`),
  KEY `index_events_on_listing_id` (`listing_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6359 DEFAULT CHARSET=utf8;



# Dump of table listings
# ------------------------------------------------------------

CREATE TABLE `listings` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `latitude` decimal(15,10) DEFAULT NULL,
  `longitude` decimal(15,10) DEFAULT NULL,
  `description` text,
  PRIMARY KEY (`id`),
  KEY `index_listings_on_place` (`name`),
  KEY `index_listings_on_latitude_and_longitude` (`latitude`,`longitude`)
) ENGINE=InnoDB AUTO_INCREMENT=109358 DEFAULT CHARSET=utf8;



# Dump of table users
# ------------------------------------------------------------

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `email` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_users_on_email` (`email`),
  KEY `index_users_on_latitude_and_longitude` (`latitude`,`longitude`)
) ENGINE=InnoDB AUTO_INCREMENT=1972 DEFAULT CHARSET=utf8;

Reply via email to