Home
Morgan Tocker
 
[Most Recent Entries] [Calendar View] [Friends View]

Thursday, October 23rd, 2008

    Time Event
    1:44p
    Montreal on Rails
    I spoke at Montreal on Rails on Tuesday night. I think I had 5 slides, but spoke for about 45 minutes (so there's no point in uploading them). For those that missed it (or couldn't take notes fast enough), here's a transcript of the examples I showed with the world database:

    # take a look at this query.  To start with, we have no indexes used:
    EXPLAIN SELECT Name FROM Country WHERE Continent = 'Asia' AND population > 5000000 ORDER BY Name;
    
    # First let's look at an index on population
    ALTER TABLE Country ADD INDEX p (Population);
    
    # is that index effective?
    EXPLAIN SELECT Name FROM Country WHERE Continent = 'Asia' AND population > 5000000 ORDER BY Name;
    
    # no it wasn't.  what happens if we modify the query just slightly:
    EXPLAIN SELECT Name FROM Country WHERE Continent = 'Asia' AND population > 50000000 ORDER BY Name;
    
    # time for the next index:
    ALTER TABLE Country ADD INDEX c (Continent);
    
    # with two indexes on the table, which one will the optimizer prefer?
    EXPLAIN SELECT Name FROM Country WHERE Continent = 'Asia' AND population > 50000000 ORDER BY Name;
    
    # how about now?
    EXPLAIN SELECT Name FROM Country WHERE Continent = 'Asia' AND population > 500000000 ORDER BY Name;
    
    # This index is not always helpful.  Why?
    ALTER TABLE Country ADD INDEX p_c (Population, Continent);
    
    # How about this one?
    ALTER TABLE Country ADD INDEX c_p (Continent,Population);
    
    # Why is this one better than just c_p?
    ALTER TABLE Country ADD INDEX c_p_n (Continent,Population,Name);
    
    # Remote all the indexes before trying to add an index on n.
    ALTER TABLE Country DROP INDEX p, DROP INDEX c, DROP INDEX p_c, DROP INDEX c_p, DROP INDEX c_p_n;
    ALTER TABLE Country ADD INDEX n (Name);
    
    # the optimizer still doesn't consider N.
    EXPLAIN SELECT Name FROM Country WHERE Continent = 'Asia' AND population > 500000000 ORDER BY Name;
    
    # how about now?
    EXPLAIN SELECT Name FROM Country FORCE INDEX (n) WHERE Continent = 'Asia' AND population > 500000000 ORDER BY Name;
    
    # drop the index on N.
    alter table Country drop index n;
    
    
    # SOME trick questions
    # which is better.
    
    EXPLAIN SELECT * FROM City WHERE id = 1810;
    EXPLAIN SELECT * FROM City WHERE id = 1810 LIMIT 1;
    
    # How about this one.
    
    EXPLAIN SELECT * FROM City WHERE id BETWEEN 100 and 200;
    EXPLAIN SELECT * FROM City WHERE id >= 100 and id <= 200;
    
    # (the answer is that both of the two above are identical - 
    #  they are rewritten internally to the same thing)
    
    # This is a bad subquery.
    EXPLAIN SELECT * FROM City WHERE countrycode IN (SELECT code FROM country WHERE name='Australia')
    
    # this is the rewrite as a join.
    EXPLAIN SELECT city.* FROM City, Country WHERE city.countrycode=country.code AND country.name='Australia'
    
    # does this index help?
    ALTER TABLE City ADD INDEX (countrycode);
    
    # retry
    EXPLAIN SELECT city.* FROM City, Country WHERE city.countrycode=country.code AND country.name='Australia';
    
    # add an index on city.
    ALTER TABLE Country ADD INDEX (name);
    
    # how about a retry
    EXPLAIN SELECT city.* FROM City, Country WHERE city.countrycode=country.code AND country.name='Australia';
    


    Pretty neat, huh? I teach something similar in DBA classes. I have to thank Tobias for first showing me a fair chunk of this example.

    Update Transcript of output:
    mysql> EXPLAIN SELECT Name FROM Country WHERE Continent = 'Asia' AND population > 5000000 ORDER BY Name;
    +----+-------------+---------+------+---------------+------+---------+------+------+-----------------------------+
    | id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows | Extra                       |
    +----+-------------+---------+------+---------------+------+---------+------+------+-----------------------------+
    |  1 | SIMPLE      | Country | ALL  | NULL          | NULL | NULL    | NULL |  239 | Using where; Using filesort | 
    +----+-------------+---------+------+---------------+------+---------+------+------+-----------------------------+
    1 row in set (0.00 sec)
    
    mysql> ALTER TABLE Country ADD INDEX p (Population);
    Query OK, 239 rows affected (0.01 sec)
    Records: 239  Duplicates: 0  Warnings: 0
    
    mysql> EXPLAIN SELECT Name FROM Country WHERE Continent = 'Asia' AND population > 5000000 ORDER BY Name;
    +----+-------------+---------+------+---------------+------+---------+------+------+-----------------------------+
    | id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows | Extra                       |
    +----+-------------+---------+------+---------------+------+---------+------+------+-----------------------------+
    |  1 | SIMPLE      | Country | ALL  | p             | NULL | NULL    | NULL |  239 | Using where; Using filesort | 
    +----+-------------+---------+------+---------------+------+---------+------+------+-----------------------------+
    1 row in set (0.00 sec)
    
    mysql> EXPLAIN SELECT Name FROM Country WHERE Continent = 'Asia' AND population > 50000000 ORDER BY Name;
    +----+-------------+---------+-------+---------------+------+---------+------+------+-----------------------------+
    | id | select_type | table   | type  | possible_keys | key  | key_len | ref  | rows | Extra                       |
    +----+-------------+---------+-------+---------------+------+---------+------+------+-----------------------------+
    |  1 | SIMPLE      | Country | range | p             | p    | 4       | NULL |   54 | Using where; Using filesort | 
    +----+-------------+---------+-------+---------------+------+---------+------+------+-----------------------------+
    1 row in set (0.00 sec)
    
    mysql> ALTER TABLE Country ADD INDEX c (Continent);
    Query OK, 239 rows affected (0.00 sec)
    Records: 239  Duplicates: 0  Warnings: 0
    
    mysql> EXPLAIN SELECT Name FROM Country WHERE Continent = 'Asia' AND population > 50000000 ORDER BY Name;
    +----+-------------+---------+------+---------------+------+---------+-------+------+-----------------------------+
    | id | select_type | table   | type | possible_keys | key  | key_len | ref   | rows | Extra                       |
    +----+-------------+---------+------+---------------+------+---------+-------+------+-----------------------------+
    |  1 | SIMPLE      | Country | ref  | p,c           | c    | 1       | const |   42 | Using where; Using filesort | 
    +----+-------------+---------+------+---------------+------+---------+-------+------+-----------------------------+
    1 row in set (0.00 sec)
    
    mysql> EXPLAIN SELECT Name FROM Country WHERE Continent = 'Asia' AND population > 500000000 ORDER BY Name;
    +----+-------------+---------+-------+---------------+------+---------+------+------+-----------------------------+
    | id | select_type | table   | type  | possible_keys | key  | key_len | ref  | rows | Extra                       |
    +----+-------------+---------+-------+---------------+------+---------+------+------+-----------------------------+
    |  1 | SIMPLE      | Country | range | p,c           | p    | 4       | NULL |    4 | Using where; Using filesort | 
    +----+-------------+---------+-------+---------------+------+---------+------+------+-----------------------------+
    1 row in set (0.00 sec)
    
    mysql> ALTER TABLE Country ADD INDEX p_c (Population, Continent);
    Query OK, 239 rows affected (0.00 sec)
    Records: 239  Duplicates: 0  Warnings: 0
    
    mysql> ALTER TABLE Country ADD INDEX c_p (Continent,Population);
    Query OK, 239 rows affected (0.00 sec)
    Records: 239  Duplicates: 0  Warnings: 0
    
    mysql> ALTER TABLE Country ADD INDEX c_p_n (Continent,Population,Name);
    Query OK, 239 rows affected (0.01 sec)
    Records: 239  Duplicates: 0  Warnings: 0
    
    mysql> ALTER TABLE Country DROP INDEX p, DROP INDEX c, DROP INDEX p_c, DROP INDEX c_p, DROP INDEX c_p_n;
    Query OK, 239 rows affected (0.00 sec)
    Records: 239  Duplicates: 0  Warnings: 0
    
    mysql> ALTER TABLE Country ADD INDEX n (Name);
    Query OK, 239 rows affected (0.01 sec)
    Records: 239  Duplicates: 0  Warnings: 0
    
    mysql> EXPLAIN SELECT Name FROM Country WHERE Continent = 'Asia' AND population > 500000000 ORDER BY Name;
    +----+-------------+---------+------+---------------+------+---------+------+------+-----------------------------+
    | id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows | Extra                       |
    +----+-------------+---------+------+---------------+------+---------+------+------+-----------------------------+
    |  1 | SIMPLE      | Country | ALL  | NULL          | NULL | NULL    | NULL |  239 | Using where; Using filesort | 
    +----+-------------+---------+------+---------------+------+---------+------+------+-----------------------------+
    1 row in set (0.00 sec)
    
    mysql> EXPLAIN SELECT Name FROM Country FORCE INDEX (n) WHERE Continent = 'Asia' AND population > 500000000 ORDER BY Name;
    +----+-------------+---------+-------+---------------+------+---------+------+------+-------------+
    | id | select_type | table   | type  | possible_keys | key  | key_len | ref  | rows | Extra       |
    +----+-------------+---------+-------+---------------+------+---------+------+------+-------------+
    |  1 | SIMPLE      | Country | index | NULL          | n    | 52      | NULL |  239 | Using where | 
    +----+-------------+---------+-------+---------------+------+---------+------+------+-------------+
    1 row in set (0.00 sec)
    
    mysql> alter table Country drop index n;
    Query OK, 239 rows affected (0.01 sec)
    Records: 239  Duplicates: 0  Warnings: 0
    
    mysql> EXPLAIN SELECT * FROM City WHERE id = 1810;
    +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
    | id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra |
    +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
    |  1 | SIMPLE      | City  | const | PRIMARY       | PRIMARY | 4       | const |    1 |       | 
    +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
    1 row in set (0.00 sec)
    
    mysql> EXPLAIN SELECT * FROM City WHERE id = 1810 LIMIT 1;
    +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
    | id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra |
    +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
    |  1 | SIMPLE      | City  | const | PRIMARY       | PRIMARY | 4       | const |    1 |       | 
    +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
    1 row in set (0.00 sec)
    
    mysql> EXPLAIN SELECT * FROM City WHERE id BETWEEN 100 and 200;
    +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
    | id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
    +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
    |  1 | SIMPLE      | City  | range | PRIMARY       | PRIMARY | 4       | NULL |  101 | Using where | 
    +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
    1 row in set (0.01 sec)
    
    mysql> EXPLAIN SELECT * FROM City WHERE id >= 100 and id <= 200;
    +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
    | id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
    +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
    |  1 | SIMPLE      | City  | range | PRIMARY       | PRIMARY | 4       | NULL |  101 | Using where | 
    +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
    1 row in set (0.00 sec)
    
    mysql> EXPLAIN SELECT * FROM City WHERE countrycode IN (SELECT code FROM country WHERE name='Australia');
    +----+--------------------+---------+-----------------+---------------+---------+---------+------+------+-------------+
    | id | select_type        | table   | type            | possible_keys | key     | key_len | ref  | rows | Extra       |
    +----+--------------------+---------+-----------------+---------------+---------+---------+------+------+-------------+
    |  1 | PRIMARY            | City    | ALL             | NULL          | NULL    | NULL    | NULL | 4079 | Using where | 
    |  2 | DEPENDENT SUBQUERY | country | unique_subquery | PRIMARY       | PRIMARY | 3       | func |    1 | Using where | 
    +----+--------------------+---------+-----------------+---------------+---------+---------+------+------+-------------+
    2 rows in set (0.00 sec)
    
    mysql> EXPLAIN SELECT city.* FROM City, Country WHERE city.countrycode=country.code AND country.name='Australia';
    +----+-------------+---------+--------+---------------+---------+---------+------------------------+------+-------------+
    | id | select_type | table   | type   | possible_keys | key     | key_len | ref                    | rows | Extra       |
    +----+-------------+---------+--------+---------------+---------+---------+------------------------+------+-------------+
    |  1 | SIMPLE      | City    | ALL    | NULL          | NULL    | NULL    | NULL                   | 4079 |             | 
    |  1 | SIMPLE      | Country | eq_ref | PRIMARY       | PRIMARY | 3       | world.City.CountryCode |    1 | Using where | 
    +----+-------------+---------+--------+---------------+---------+---------+------------------------+------+-------------+
    2 rows in set (0.00 sec)
    
    mysql> ALTER TABLE City ADD INDEX (countrycode);
    Query OK, 4079 rows affected (0.03 sec)
    Records: 4079  Duplicates: 0  Warnings: 0
    
    mysql> EXPLAIN SELECT city.* FROM City, Country WHERE city.countrycode=country.code AND country.name='Australia';
    +----+-------------+---------+--------+---------------+---------+---------+------------------------+------+-------------+
    | id | select_type | table   | type   | possible_keys | key     | key_len | ref                    | rows | Extra       |
    +----+-------------+---------+--------+---------------+---------+---------+------------------------+------+-------------+
    |  1 | SIMPLE      | City    | ALL    | CountryCode   | NULL    | NULL    | NULL                   | 4079 |             | 
    |  1 | SIMPLE      | Country | eq_ref | PRIMARY       | PRIMARY | 3       | world.City.CountryCode |    1 | Using where | 
    +----+-------------+---------+--------+---------------+---------+---------+------------------------+------+-------------+
    2 rows in set (0.00 sec)
    
    mysql> ALTER TABLE Country ADD INDEX (name);
    Query OK, 239 rows affected (0.00 sec)
    Records: 239  Duplicates: 0  Warnings: 0
    
    mysql> EXPLAIN SELECT city.* FROM City, Country WHERE city.countrycode=country.code AND country.name='Australia';
    +----+-------------+---------+------+---------------+-------------+---------+--------------------+------+-------------+
    | id | select_type | table   | type | possible_keys | key         | key_len | ref                | rows | Extra       |
    +----+-------------+---------+------+---------------+-------------+---------+--------------------+------+-------------+
    |  1 | SIMPLE      | Country | ref  | PRIMARY,Name  | Name        | 52      | const              |    1 | Using where | 
    |  1 | SIMPLE      | City    | ref  | CountryCode   | CountryCode | 3       | world.Country.Code |   18 |             | 
    +----+-------------+---------+------+---------------+-------------+---------+--------------------+------+-------------+
    2 rows in set (0.01 sec)
    
    mysql> 
    

    << Previous Day 2008/10/23
    [Calendar]
    Next Day >>

Morgan Tocker's Blog   About LiveJournal.com

Advertisement