views:

178

answers:

5

I have two tables, one for routes and one for airports.

Routes contains just over 9000 rows and I have indexed every column. Airports only 2000 rows and I have also indexed every column.

When I run this query it can take up to 35 seconds to return 300 rows:

SELECT routes.* , a1.name as origin_name, a2.name as destination_name FROM routes
LEFT JOIN airports a1 ON a1.IATA = routes.origin
LEFT JOIN airports a2 ON a2.IATA = routes.destination
WHERE routes_build.carrier = "Carrier Name"

Running it with "DESCRIBE" I get the followinf info, but I'm not 100% sure on what it's telling me.

id | Select Type   | Table             | Type   | possible_keys        | Key            | Key_len | ref  | rows  | Extra
--------------------------------------------------------------------------------------------------------------------------------------
1  | SIMPLE        | routes_build      | ref | carrier,carrier_2    | carrier     | 678  | const  | 26  | Using where
--------------------------------------------------------------------------------------------------------------------------------------
1  | SIMPLE        | a1                | ALL | NULL                | NULL         | NULL  | NULL  | 5389  |
--------------------------------------------------------------------------------------------------------------------------------------
1  | SIMPLE        | a2                | ALL | NULL                | NULL         | NULL  | NULL  | 5389  |
--------------------------------------------------------------------------------------------------------------------------------------

The only alternative I can think of is to run two separate queries and join them up with PHP although, I can't believe something like this being something that could kill a mysql server. So as usual, I suspect I'm doing something stupid. SQL is my number 1 weakness.

+3  A: 

Personally, I would start by removing the left joins and replacing them with inner joins as each route must have a start and end point.

Martin Robins
So how do I handle the Aliases. I've tried this...SELECT routes.* , a1.name as origin_name, a2.name as destination_name FROM routes_build, airports as a1, airports as a2WHERE a1.IATA = routes_build.originAND a1.IATA = routes_build.destinationAND routes_build.carrier = 'Ryanair'But I'm get no results.
gargantaun
Sorry about that, S.O scrapped all the nice formatting.
gargantaun
just take out the word LEFT and your original SQL should run
James
Ahhhhh, thanks. 35 seconds down to 919ms. Perfik! I'm going to have to knuckle down with a MySQL book sometime soon.
gargantaun
A: 

After you implement Martin Robins's excellent advice (i.e. remove every instance of the word LEFT from your query), try giving routes_build a compound index on carrier, origin, and destination.

chaos
A: 

It really depends on what information you're trying to get to. You probably don't need to join airports twice and you probably don't need to use left joins. Also, if you can search on a numeric field rather than a text field, that would speed things up as well.

So what are you trying to fetch?

Cameron
I'm trying to get a route but it's airport codes, and also the airport names. I have the join because I need to get the airport name twice. Once for the origin, and once for the destination.
gargantaun
+1  A: 
SELECT  routes.*, a1.name as origin_name, a2.name as destination_name
FROM    routes_build
LEFT JOIN
        airports a1
ON      a1.IATA = routes_build.origin
LEFT JOIN
        airports a2
ON      a2.IATA = routes_build.destination
WHERE   routes_build.carrier = "Carrier Name"

From your EXPLAIN PLAN I can see that you don't have an index on airports.IATA.

You should create it for the query to work fast.

Name also suggests that it should be a UNIQUE index, since IATA codes are unique.

Update:

Please post your table definition. Issue this query to show it:

SHOW CREATE TABLE airports

Also I should note that your FULLTEXT index on IATA is useless unless you have set ft_max_word_len is MySQL configuration to 3 or less.

By default, it's 4.

IATA codes are 3 characters long, and MySQL doesn't search for such short words using FULLTEXT with default settings.

Quassnoi
Question claims that he's indexed every column in airports. But yeah, the explain looks like it isn't indexed. Something odd going on there.
chaos
That's odd because I do have an index on airports.IATA, in fact I have two, an Index and a Full Text index.
gargantaun
Could you please post your table definition: SHOW CREATE TABLE airports;
Quassnoi
I don't know how the indexing works internally on MySQL but the Full Text index could slow down a join. Typcailly full text indexes are based on lexical analysis meaning this could be similar to using "a1.IATA LIKE '%' + routes_build.origin + '%'"
Matthew Whited
@Matthew: Shouldn't make a difference: you can see in his explain output no index is being used. It's loading all the rows by hand
James
CREATE TABLE `airports` ( `id` int(11) NOT NULL auto_increment, `name` varchar(225) default NULL, `city` varchar(225) default NULL, `country` varchar(225) default NULL, `IATA` varchar(3) default NULL, `ICAO` varchar(4) default NULL, `lat` float default NULL, `lon` float default NULL, `commercial` int(1) default '0', `borders` varchar(2) default NULL, `last_check` bigint(20) NOT NULL default '1244523500', PRIMARY KEY (`id`), KEY `IATA` (`IATA`), KEY `country` (`country`), KEY `commercial` (`commercial`)) ENGINE=MyISAM AUTO_INCREMENT=13402 DEFAULT CHARSET=latin1
gargantaun
Also, the full text index is useless and I have since deleted it. I tried it on the off chance it might make a difference and only mentioned it to confirm that I did have two indexes on the same column.
gargantaun
+1  A: 

It's telling you that it's not using an index for joining on the airports table. See how the "rows" column is so huge, 5000 odd? that's how many rows it's having to read to answer your query.

I don't know why, as you have claimed you have indexed every column. What is IATA? Is it Unique? I believe if mysql decides the index is inefficient it may ignore it.

EDIT: if IATA is a unique string, maybe try indexing half of it only? (You can select how many characters to index) That may give mysql an index it can use.

James
Ahh, so IATA is a unique string. So your index is useless because your index has exactly the same number of entries as your table. You need to get to a situation where your index has substancially less rows than your table, because then mysql will get a speed increase from using it. Try indexing half the IATA column only. eg airport codes are 4 chars, if I recall, so only index 2 characters.
James
Yes, IATA is unique in airports, but not in routes.
gargantaun
I'm talking about the IATA column on the Airport table - sorry for not making that clear
James
no worries. You actually pointed out a big problem with my data as some airports shared the same IATA code. Sometimes it was a mistake, other times it's a problem with the IATA and FAA systems. Anyway, I went through and fixed all the duplicates, added a Unique Index and got the query down from 919ms to 425ms. Thanks for that.
gargantaun