EXPLAIN: missing db info

I’m further developing a general log hook, which can stream queries from the general log.

A particular direction I’m taking is to filter queries by their type of actions. For example, the tool (oak-hook-general-log) can be instructed to only stream out those queries which involve creation of a temporary table; or those which cause for a filesort, or full index scan, etc.

This is done by evaluating of query execution plans on the fly. I suspect the MySQL query analyzer roughly does the same (as a small part of what it does).

It’s almost nothing one cannot do with sed/awk. However, I bumped into a couple of problems:

  1. The general log (and the mysql.general_log table, in  particular) does not indicate the particular database one is using for the query. Since slow log does indicate this data, I filed a bug on this. I currently solve this by crossing connection id with the process list, where the current database is listed. It’s shaky, but mostly works.
  2. Just realized: there’s no DB info in the EXPLAIN output! It’s weird, since I’ve been EXPLAINing queries for years now. But I’ve always had the advantage of knowing the schema used: either because I was manually executing the query on a known schema, or mk-query-digest was kind enough to let me know.

For example, look at the following imaginary query, involving both the world and sakila databases:

mysql> use test;
Database changed
mysql> EXPLAIN SELECT * FROM world.Country JOIN sakila.city WHERE Country.Capital = city.city_id;
+----+-------------+---------+--------+---------------+---------+---------+-----------------------+------+-------------+
| id | select_type | table   | type   | possible_keys | key     | key_len | ref                   | rows | Extra       |
+----+-------------+---------+--------+---------------+---------+---------+-----------------------+------+-------------+
|  1 | SIMPLE      | Country | ALL    | NULL          | NULL    | NULL    | NULL                  |  239 |             |
|  1 | SIMPLE      | city    | eq_ref | PRIMARY       | PRIMARY | 2       | world.Country.Capital |    1 | Using where |
+----+-------------+---------+--------+---------------+---------+---------+-----------------------+------+-------------+
2 rows in set (0.00 sec)

The query is imaginary, since the tables don’t actually have anything in common. But look at the EXPLAIN result: can you tell where city came from? Country can somehow be parsed from the ref column, but no help on city.

Moreover, table aliases show on the EXPLAIN plan (which is good), but with no reference to the original table.

So, is it back to parsing of the SQL query? I’m lazy reluctant to do that. It’s error prone, and one needs to implement, or use, a good parser, which also accepts MySQL dialect. Haven’t looked into this yet.

I’m currently at a standstill with regard to automated query execution plan evaluation where database cannot be determined.

13 thoughts on “EXPLAIN: missing db info

  1. Take a look at what mk-index-usage does. There is a lot of overlap between your case and what that tool does. Yes, there is a lot of SQL parsing, and it is not easy. We have a lot of test cases.

  2. Roland,

    It still does not help in matching the query against the execution plan.

    mysql> explain extended select id from world.City ci, (select code from world.Country co) s1;
    +----+-------------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------------+
    | id | select_type | table      | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra                          |
    +----+-------------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------------+
    |  1 | PRIMARY     | <derived2> | ALL   | NULL          | NULL    | NULL    | NULL |  239 |   100.00 |                                |
    |  1 | PRIMARY     | ci         | index | NULL          | PRIMARY | 4       | NULL | 4079 |   100.00 | Using index; Using join buffer |
    |  2 | DERIVED     | co         | index | NULL          | PRIMARY | 3       | NULL |  239 |   100.00 | Using index                    |
    +----+-------------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------------+
    
    
    

    How can I guess the <derived2> alias and match it against the query? How about two derived tables?

    You are right; I don’t have to go to that depth; but the job becomes very difficult. I suspect there are other examples.

  3. @Baron,

    Thanks; I’ve just seen it. I think mk-query-digest already uses a very smart SQL parser.
    If I happen to work on my perl skills, I’ll take a better look.

  4. @shlomi,

    “How can I guess the alias and match it against the query?”

    I think I picked this up during Baron’s “Explain Demystified” talk from two years back at the MySQL user conference.

    Assuming you can parse the SQL and succesfully match the ci and co aliases in the explain output to the query parse tree, you then know that `co` is a DERIVED table. Together with the value in the id column for that table, you get “derived2” (as in, CONCAT(LOWER(select_type), id)

    Try it with another query like this:

    explain extended select id from world.City ci, (select code from (select * from world.Country) co) s1;

    You’ll notice that it actually works.

    Of course, don’t mistake my explanation as approval – this is an ugly mess, at least for what you want to achieve 🙂

  5. @Roland,
    Hey, that’s pretty cool! I may play around with this (or maybe not just right now). I’ll let you know if I happen to get results.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.