EXPLAIN EXTENDED

How to create fast database queries

My latest article on SQL in general: Happy New Year!. You're welcome to read and comment on it.

Sorting lists

This is article 1 of 6 on linked lists in MySQL:

From Stack Overflow:

I have an app which has tasks in it and you can reorder them.

Now I was wondering how to best store them. Should I have a column for the order number and recalculate all of them everytime I change one?

Please tell me a version which doesn’t require me to update all order numbers since that is very time consuming (from the execution’s point of view).

It’s probably better to keep it in a linked list:

SELECT  *
FROM    t_list
ORDER BY
        id
id parent
1 0
2 3
3 4
4 1
4 rows fetched in 0.0002s (0.0016s)

To return the ordered list, we query it like this:

SELECT  @r := (
        SELECT  id
        FROM    t_list
        WHERE   parent = @r
        ) AS id
FROM    (
        SELECT  @r := 0
        ) vars,
        t_list
id
1
4
3
2
4 rows fetched in 0.0001s (0.0013s)

Note that if we look into the query execution plan, we will note, that the index on parent is not used for value lookup:

EXPLAIN EXTENDED
SELECT  @r := (
        SELECT  id
        FROM    t_list
        WHERE   parent = @r
        ) AS id
FROM    (
        SELECT  @r := 0
        ) vars,
        t_list
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY <derived3> system 1 100.00
1 PRIMARY t_list index PRIMARY 4 4 100.00 Using index
3 DERIVED No tables used
2 UNCACHEABLE SUBQUERY t_list index ix_list_parent 4 4 100.00 Using where; Using index
4 rows fetched in 0.0022s (0.0029s)

Note there is no ref in the UNCACHEABLE SUBQUERY.

This is because MySQL does not treat a variable as a constant.

To work around this, we can select the variable earlier in the SELECT clause, and just refer to the field it produces:

SELECT  @r AS _parent,
        @r := (
        SELECT  id
        FROM    t_list
        WHERE   parent = _parent
        ) AS id
FROM    (
        SELECT  @r := 0
        ) vars,
        t_list
_parent id
0 1
1 4
4 3
3 2
4 rows fetched in 0.0012s (0.0012s)

The plan now looks like this:

EXPLAIN EXTENDED
SELECT  @r AS _parent,
        @r := (
        SELECT  id
        FROM    t_list
        WHERE   parent = _parent
        ) AS id
FROM    (
        SELECT  @r := 0
        ) vars,
        t_list
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY <derived3> system 1 100.00
1 PRIMARY t_list index PRIMARY 4 4 100.00 Using index
3 DERIVED No tables used
2 DEPENDENT SUBQUERY t_list eq_ref ix_list_parent ix_list_parent 4 func 1 100.00 Using where; Using index
4 rows fetched in 0.0020s (0.0030s)

The subquery became a DEPENDENT SUBQUERY now, and the index is being used as it should.

To be continued.

Written by Quassnoi

March 25th, 2009 at 11:00 pm

Posted in MySQL

Comments are closed.