I need your help optimizing a query. One table is a log table which has millions of entries and I try to break my query to < 1s. My query should give an overall overview and should be quick therefore. I'm sure I could make multiple simple queries over the list with help of a script. But what a script can do, can mysql I think - I hope at least. And maybe not all parts are best used, but I'm stuck in a query which makes a temporary table and filesort (which I found out is really bad). As reading around I found out to use some neat and well placed indexes but now I'm stuck in a specific point.
Let me show you my final query with its results:
SELECT
ps.SERVER_ID,
ps.FULLNAME,
SUM(CASE WHEN pml.ID_TYPE = 3 THEN 1 ELSE 0 END) 'amount_warning',
SUM(CASE WHEN pml.ID_TYPE = 4 THEN 1 ELSE 0 END) 'amount_error',
SUM(CASE WHEN pml.ID_TYPE = 5 THEN 1 ELSE 0 END) 'amount_alert',
SUM(CASE WHEN pml.ID_TYPE = 7 THEN 1 ELSE 0 END) 'amount_critical'
FROM
PAR_SERVER ps
INNER JOIN
PAR_MONITORINGv2_LOG pml ON ps.SERVER_ID = pml.SERVER_ID
WHERE
pml.CREATED_DATE > date_sub( NOW( ) , INTERVAL 7 DAY )
GROUP BY
ps.SERVER_ID;
Here is what I get:
mysql> [thequeryabove]
[...]
59 rows in set (11.69 sec)
mysql> explain [thequeryabove]
+----+-------------+-------+--------+-----------------------------+---------+---------+---------------------------+---------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+-----------------------------+---------+---------+---------------------------+---------+----------------------------------------------+
| 1 | SIMPLE | pml | ALL | SERVER_ID,SERVER_ID-ID_TYPE | NULL | NULL | NULL | 4014447 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | ps | eq_ref | PRIMARY | PRIMARY | 4 | database.pml.SERVER_ID | 1 | |
+----+-------------+-------+--------+-----------------------------+---------+---------+---------------------------+---------+----------------------------------------------+
2 rows in set (0.00 sec)
Here's my current table setup:
mysql> describe PAR_SERVER;
+----------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+--------------+------+-----+---------+----------------+
| SERVER_ID | int(255) | NO | PRI | NULL | auto_increment |
| FULLNAME | varchar(255) | YES | | NULL | |
| SHORTNAME | varchar(255) | YES | MUL | NULL | |
+----------------+--------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
mysql> show indexes from PAR_SERVER;
+------------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+------------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| PAR_SERVER | 0 | PRIMARY | 1 | SERVER_ID | A | 142 | NULL | NULL | | BTREE | |
| PAR_SERVER | 1 | shortname | 1 | SHORTNAME | A | 142 | NULL | NULL | YES | BTREE | |
+------------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
2 rows in set (0.00 sec)
mysql> select count(*) from PAR_SERVER;
+----------+
| count(*) |
+----------+
| 142 |
+----------+
1 row in set (0.00 sec)
mysql> describe PAR_MONITORINGv2_LOG;
+--------------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+----------+------+-----+---------+----------------+
| ID | int(11) | NO | PRI | NULL | auto_increment |
| ID_TYPE | int(11) | NO | MUL | NULL | |
| ID_SERVICE | int(11) | NO | MUL | NULL | |
| SERVER_ID | int(11) | NO | MUL | NULL | |
| MESSAGE | tinytext | NO | | NULL | |
| CREATED_DATE | datetime | NO | | NULL | |
+--------------+----------+------+-----+---------+----------------+
6 rows in set (0.00 sec)
mysql> show indexes from PAR_MONITORINGv2_LOG;
+----------------------+------------+-------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+----------------------+------------+-------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| PAR_MONITORINGv2_LOG | 0 | PRIMARY | 1 | ID | A | 3998188 | NULL | NULL | | BTREE | |
| PAR_MONITORINGv2_LOG | 1 | ID_TYPE | 1 | ID_TYPE | A | 7 | NULL | NULL | | BTREE | |
| PAR_MONITORINGv2_LOG | 1 | ID_SERVICE | 1 | ID_SERVICE | A | 5 | NULL | NULL | | BTREE | |
| PAR_MONITORINGv2_LOG | 1 | SERVER_ID | 1 | SERVER_ID | A | 66 | NULL | NULL | | BTREE | |
| PAR_MONITORINGv2_LOG | 1 | SERVER_ID-ID_TYPE | 1 | SERVER_ID | A | 66 | NULL | NULL | | BTREE | |
| PAR_MONITORINGv2_LOG | 1 | SERVER_ID-ID_TYPE | 2 | ID_TYPE | A | 258 | NULL | NULL | | BTREE | |
+----------------------+------------+-------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
6 rows in set (0.00 sec)
mysql> select count(*) from PAR_MONITORINGv2_LOG;
+----------+
| count(*) |
+----------+
| 3998386 |
+----------+
1 row in set (0.00 sec)
Here are time results breaking my query step by step down. I may going step by step up after fixing each part taking so long. But for now only the query with runtime of 2.30 sec is currently interesting for this question.
mysql> SELECT ps.SERVER_ID, ps.FULLNAME FROM PAR_SERVER ps INNER JOIN PAR_MONITORINGv2_LOG pml ON ps.SERVER_ID = pml.SERVER_ID WHERE pml.CREATED_DATE > date_sub( NOW( ) , INTERVAL 7 DAY ) GROUP BY ps.SERVER_ID;
[...]
59 rows in set (6.41 sec)
mysql> explain [thequeryabove]
+----+-------------+-------+--------+-----------------------------+---------+---------+---------------------------+---------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+-----------------------------+---------+---------+---------------------------+---------+----------------------------------------------+
| 1 | SIMPLE | pml | ALL | SERVER_ID,SERVER_ID-ID_TYPE | NULL | NULL | NULL | 4014788 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | ps | eq_ref | PRIMARY | PRIMARY | 4 | database.pml.SERVER_ID | 1 | |
+----+-------------+-------+--------+-----------------------------+---------+---------+---------------------------+---------+----------------------------------------------+
2 rows in set (0.00 sec)
mysql> SELECT ps.SERVER_ID, ps.FULLNAME FROM PAR_SERVER ps INNER JOIN PAR_MONITORINGv2_LOG pml ON ps.SERVER_ID = pml.SERVER_ID GROUP BY ps.SERVER_ID;
[...]
59 rows in set (2.30 sec)
mysql> explain [thequeryabove]
+----+-------------+-------+--------+-----------------------------+-----------+---------+---------------------------+---------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+-----------------------------+-----------+---------+---------------------------+---------+----------------------------------------------+
| 1 | SIMPLE | pml | index | SERVER_ID,SERVER_ID-ID_TYPE | SERVER_ID | 4 | NULL | 4015694 | Using index; Using temporary; Using filesort |
| 1 | SIMPLE | ps | eq_ref | PRIMARY | PRIMARY | 4 | database.pml.SERVER_ID | 1 | |
+----+-------------+-------+--------+-----------------------------+-----------+---------+---------------------------+---------+----------------------------------------------+
2 rows in set (0.00 sec)
mysql> SELECT pml.SERVER_ID FROM PAR_MONITORINGv2_LOG pml GROUP BY pml.SERVER_ID;
[...]
65 rows in set (0.00 sec)
mysql> explain [thequeryabove]
+----+-------------+-------+-------+---------------+-----------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+--------------------------+
| 1 | SIMPLE | pml | range | NULL | SERVER_ID | 4 | NULL | 67 | Using index for group-by |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+--------------------------+
1 row in set (0.00 sec)
I was able to improve the query a lot by defining an index for (SERVER_ID, ID_TYPE) as my following example query confirms:
mysql> SELECT count(*) 'count_warnings' FROM PAR_MONITORINGv2_LOG pml WHERE pml.SERVER_ID = 191 AND pml.ID_TYPE = 3 GROUP BY pml.SERVER_ID;
[...]
1 row in set (0.01 sec)
mysql> explain [thequeryabove]
+----+-------------+-------+------+-------------------------------------+-------------------+---------+-------------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+-------------------------------------+-------------------+---------+-------------+-------+-------------+
| 1 | SIMPLE | pml | ref | ID_TYPE,SERVER_ID,SERVER_ID-ID_TYPE | SERVER_ID-ID_TYPE | 8 | const,const | 10254 | Using index |
+----+-------------+-------+------+-------------------------------------+-------------------+---------+-------------+-------+-------------+
1 row in set (0.00 sec)
I'm stuck now in the most broked down query with a long execution time of 2.30 sec. I don't know how to use indexes for such a query not having any where clause.
Your query will definitely benefit the most from adding composite index on PAR_MONITORINGv2_LOG(CREATED_DATE, SERVER_ID,ID_TYPE). However, I suggest even simple index on CREATED_DATE will improve performance a lot.
Related
We have three tables users, stores and store_customer_associations, where a user can be a customer for many stores.
I've tried to place another index in primary key but without success (and other too).
(currently these are our indexes in each table)
mysql> SHOW INDEXES FROM store_customer_associations;
+-------+------------+-------------------------------------+--------------+----------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+-------------------------------------+--------------+----------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| users | 0 | PRIMARY | 1 | id | A | 9386515 | NULL | NULL | | BTREE | | |
| users | 0 | tmp_idx_users_id | 1 | id | A | 9386515 | NULL | NULL | | BTREE | | |
+-------+------------+-------------------------------------+--------------+----------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
10 rows in set (0.00 sec)
mysql> SHOW INDEXES FROM store_customer_associations;
+-----------------------------+------------+-----------------------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------------------------+------------+-----------------------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| store_customer_associations | 0 | PRIMARY | 1 | id | A | 51298761 | NULL | NULL | | BTREE | | |
| store_customer_associations | 1 | index_store_customer_associations_on_store_id | 1 | store_id | A | 50096 | NULL | NULL | YES | BTREE | | |
| store_customer_associations | 1 | index_store_customer_associations_on_user_id | 1 | user_id | A | 25649380 | NULL | NULL | YES | BTREE | | |
+-----------------------------+------------+-----------------------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.00 sec)
We are using rails 5.1 (this is a big rails 4 legacy database).
Then we need to make this query fast:
SELECT COUNT(*)
FROM `users`
INNER JOIN `store_customer_associations`
ON `users`.`id` = `store_customer_associations`.`user_id`
WHERE `store_customer_associations`.`store_id` = STORE_ID;
+----------+
| COUNT(*) |
+----------+
| 1997632 |
+----------+
1 row in set (6.64 sec)
mysql> EXPLAIN SELECT COUNT(*)
FROM `users`
INNER JOIN `store_customer_associations`
ON `users`.`id` = `store_customer_associations`.`user_id`
WHERE `store_customer_associations`.`store_id` = STORE_ID;
+------+-------------+-----------------------------+--------+--------------------------------------------------------------------------------------------+-----------------------------------------------+---------+---------------------------------------------------------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-----------------------------+--------+--------------------------------------------------------------------------------------------+-----------------------------------------------+---------+---------------------------------------------------------+---------+-------------+
| 1 | SIMPLE | store_customer_associations | ref | index_store_customer_associations_on_store_id,index_store_customer_associations_on_user_id | index_store_customer_associations_on_store_id | 5 | const | 4401812 | Using where |
| 1 | SIMPLE | users | eq_ref | PRIMARY,tmp_idx_users_id | PRIMARY | 4 | trustvox_production.store_customer_associations.user_id | 1 | Using index |
+------+-------------+-----------------------------+--------+--------------------------------------------------------------------------------------------+-----------------------------------------------+---------+---------------------------------------------------------+---------+-------------+
2 rows in set (0.00 sec)
UPDATE 1
mysql> EXPLAIN SELECT COUNT(*)
FROM `users`
INNER JOIN `store_customer_associations`
ON `users`.`id` = `store_customer_associations`.`user_id`
WHERE `store_customer_associations`.`store_id` = STORE_ID;
+------+-------------+-----------------------------+--------+-------------------------------------------------------------------------------------------------------------+-----------------------------------------------+---------+---------------------------------------------------------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-----------------------------+--------+-------------------------------------------------------------------------------------------------------------+-----------------------------------------------+---------+---------------------------------------------------------+---------+-------------+
| 1 | SIMPLE | store_customer_associations | ref | index_store_customer_associations_on_store_id,index_store_customer_associations_on_user_id,user_id_store_id | index_store_customer_associations_on_store_id | 5 | const | 4401812 | Using where |
| 1 | SIMPLE | users | eq_ref | PRIMARY,tmp_idx_users_id | PRIMARY | 4 | trustvox_production.store_customer_associations.user_id | 1 | Using index |
+------+-------------+-----------------------------+--------+-------------------------------------------------------------------------------------------------------------+-----------------------------------------------+---------+---------------------------------------------------------+---------+-------------+
2 rows in set (0.00 sec)
UPDATE 2
mysql> SELECT COUNT(*)
FROM users
INNER JOIN store_customer_associations
FORCE INDEX FOR JOIN (PRIMARY, user_id_store_id)
ON users.id = store_customer_associations.user_id
WHERE store_customer_associations.store_id = STORE_ID;
+----------+
| COUNT(*) |
+----------+
| 1997632 |
+----------+
1 row in set (28.90 sec)
mysql> EXPLAIN SELECT COUNT(*)
FROM users
INNER JOIN store_customer_associations
FORCE INDEX FOR JOIN (PRIMARY, user_id_store_id)
ON users.id = store_customer_associations.user_id
WHERE store_customer_associations.store_id = STORE_ID;
+------+-------------+-----------------------------+-------+--------------------------+------------------+---------+------------------------------------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-----------------------------+-------+--------------------------+------------------+---------+------------------------------------+---------+-------------+
| 1 | SIMPLE | users | index | PRIMARY,tmp_idx_users_id | tmp_idx_users_id | 4 | NULL | 8675689 | Using index |
| 1 | SIMPLE | store_customer_associations | ref | user_id_store_id | user_id_store_id | 10 | trustvox_production.users.id,const | 1 | Using index |
+------+-------------+-----------------------------+-------+--------------------------+------------------+---------+------------------------------------+---------+-------------+
2 rows in set (0.00 sec)
mysql>
UPDATE 3
mysql> EXPLAIN SELECT COUNT(*)
FROM users
INNER JOIN
( SELECT *
FROM store_customer_associations
WHERE store_id = 75856
) sca ON users.id = sca.user_id;
+------+-------------+-----------------------------+--------+-------------------------------------------------------------------------------------------------------------+-----------------------------------------------+---------+---------------------------------------------------------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-----------------------------+--------+-------------------------------------------------------------------------------------------------------------+-----------------------------------------------+---------+---------------------------------------------------------+---------+-------------+
| 1 | SIMPLE | store_customer_associations | ref | index_store_customer_associations_on_store_id,index_store_customer_associations_on_user_id,user_id_store_id | index_store_customer_associations_on_store_id | 5 | const | 4401812 | Using where |
| 1 | SIMPLE | users | eq_ref | PRIMARY,tmp_idx_users_id | PRIMARY | 4 | trustvox_production.store_customer_associations.user_id | 1 | Using index |
+------+-------------+-----------------------------+--------+-------------------------------------------------------------------------------------------------------------+-----------------------------------------------+---------+---------------------------------------------------------+---------+-------------+
UPDATE 4
mysql> ALTER TABLE store_customer_associations DROP INDEX index_store_customer_associations_on_store_id;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> ALTER TABLE store_customer_associations DROP INDEX index_store_customer_associations_on_user_id;
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> CREATE INDEX index_on_store_id_and_user_id ON store_customer_associations (store_id, user_id);
Query OK, 0 rows affected (45.95 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> CREATE INDEX index_store_customer_associations_on_user_id ON store_customer_associations(user_id);
Query OK, 0 rows affected (33.06 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> CREATE INDEX index_store_customer_associations_on_store_id ON store_customer_associations(store_id);
Query OK, 0 rows affected (38.58 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> SHOW INDEXES FROM store_customer_associations;
+-----------------------------+------------+-----------------------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------------------------+------------+-----------------------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| store_customer_associations | 0 | PRIMARY | 1 | id | A | 10305620 | NULL | NULL | | BTREE | | |
| store_customer_associations | 1 | index_on_store_id_and_user_id | 1 | store_id | A | 8244 | NULL | NULL | YES | BTREE | | |
| store_customer_associations | 1 | index_on_store_id_and_user_id | 2 | user_id | A | 10305620 | NULL | NULL | YES | BTREE | | |
| store_customer_associations | 1 | index_store_customer_associations_on_user_id | 1 | user_id | A | 10305620 | NULL | NULL | YES | BTREE | | |
| store_customer_associations | 1 | index_store_customer_associations_on_store_id | 1 | store_id | A | 6424 | NULL | NULL | YES | BTREE | | |
+-----------------------------+------------+-----------------------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
5 rows in set (0.00 sec)
store_customer_associations: INDEX(store_id, user_id) -- "covering"
store_id is first -- to handle the WHERE; user_id is added so that the only the index need by touched.
A tutorial that lets you discover the INDEX yourself: http://mysql.rjweb.org/doc.php/index_cookbook_mysql
U need to make index for 2 parameters for store_curstometer_associations because in query index is not because where part.
create index user_id_store_id on store_customer_associations (user_id, store_id)
Should help u out
Example
mysql> show indexes from api_plexts;
+------------+------------+----------------------------------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------+------------+----------------------------------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| api_plexts | 0 | PRIMARY | 1 | id | A | 206318 | NULL | NULL | | BTREE | | |
| api_plexts | 0 | index_api_plexts_on_guid | 1 | guid | A | 215475 | NULL | NULL | YES | BTREE | | |
| api_plexts | 1 | index_api_plexts_on_api_agent_id | 1 | api_agent_id | A | 1565 | NULL | NULL | YES | BTREE | | |
| api_plexts | 1 | index_api_plexts_on_from_api_portal_id | 1 | from_api_portal_id | A | 11401 | NULL | NULL | YES | BTREE | | |
| api_plexts | 1 | index_api_plexts_on_to_api_portal_id | 1 | to_api_portal_id | A | 9566 | NULL | NULL | YES | BTREE | | |
+------------+------------+----------------------------------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
5 rows in set (0.00 sec)
mysql> show indexes from api_portals;
+-------------+------------+--------------------------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------------+------------+--------------------------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| api_portals | 0 | PRIMARY | 1 | id | A | 20530 | NULL | NULL | | BTREE | | |
| api_portals | 0 | index_api_portals_on_guid | 1 | guid | A | 19891 | NULL | NULL | YES | BTREE | | |
| api_portals | 1 | index_api_portals_on_owner_agent_id | 1 | owner_agent_id | A | 718 | NULL | NULL | YES | BTREE | | |
| api_portals | 1 | index_api_portals_on_api_map_tile_id | 1 | api_map_tile_id | A | 5480 | NULL | NULL | YES | BTREE | | |
+-------------+------------+--------------------------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
explain select count(*) from api_portals ap JOIN api_plexts al FORCE INDEX FOR JOIN (PRIMARY) on ap.id = al.from_api_portal_id where al.api_agent_id = 2\G;
mysql> show indexes from api_plexts;
+------------+------------+----------------------------------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------+------------+----------------------------------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| api_plexts | 0 | PRIMARY | 1 | id | A | 206318 | NULL | NULL | | BTREE | | |
| api_plexts | 0 | index_api_plexts_on_guid | 1 | guid | A | 215475 | NULL | NULL | YES | BTREE | | |
| api_plexts | 1 | index_api_plexts_on_api_agent_id | 1 | api_agent_id | A | 1565 | NULL | NULL | YES | BTREE | | |
| api_plexts | 1 | index_api_plexts_on_from_api_portal_id | 1 | from_api_portal_id | A | 11401 | NULL | NULL | YES | BTREE | | |
| api_plexts | 1 | index_api_plexts_on_to_api_portal_id | 1 | to_api_portal_id | A | 9566 | NULL | NULL | YES | BTREE | | |
+------------+------------+----------------------------------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
5 rows in set (0.00 sec)
mysql> show indexes from api_portals;
+-------------+------------+--------------------------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------------+------------+--------------------------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| api_portals | 0 | PRIMARY | 1 | id | A | 20530 | NULL | NULL | | BTREE | | |
| api_portals | 0 | index_api_portals_on_guid | 1 | guid | A | 19891 | NULL | NULL | YES | BTREE | | |
| api_portals | 1 | index_api_portals_on_owner_agent_id | 1 | owner_agent_id | A | 718 | NULL | NULL | YES | BTREE | | |
| api_portals | 1 | index_api_portals_on_api_map_tile_id | 1 | api_map_tile_id | A | 5480 | NULL | NULL | YES | BTREE | | |
+-------------+------------+--------------------------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
mysql> explain select count(*) from api_portals ap JOIN api_plexts al on ap.id = al.from_api_portal_id where al.api_agent_id = 2;
+----+-------------+-------+------------+--------+-------------------------------------------------------------------------+----------------------------------+---------+-------------------------------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+-------------------------------------------------------------------------+----------------------------------+---------+-------------------------------+------+----------+--------------------------+
| 1 | SIMPLE | al | NULL | ref | index_api_plexts_on_api_agent_id,index_api_plexts_on_from_api_portal_id | index_api_plexts_on_api_agent_id | 5 | const | 271 | 100.00 | Using where |
| 1 | SIMPLE | ap | NULL | eq_ref | PRIMARY | PRIMARY | 8 | ingress.al.from_api_portal_id | 1 | 100.00 | Using where; Using index |
+----+-------------+-------+------------+--------+-------------------------------------------------------------------------+----------------------------------+---------+-------------------------------+------+----------+--------------------------+
this is exactly your case U can see using where
mysql> create index testime on api_plexts (api_agent_id, from_api_portal_id); Query OK, 0 rows affected (3.71 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show indexes from api_plexts;
+------------+------------+----------------------------------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------+------------+----------------------------------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| api_plexts | 0 | PRIMARY | 1 | id | A | 200644 | NULL | NULL | | BTREE | | |
| api_plexts | 0 | index_api_plexts_on_guid | 1 | guid | A | 209549 | NULL | NULL | YES | BTREE | | |
| api_plexts | 1 | index_api_plexts_on_api_agent_id | 1 | api_agent_id | A | 1522 | NULL | NULL | YES | BTREE | | |
| api_plexts | 1 | index_api_plexts_on_from_api_portal_id | 1 | from_api_portal_id | A | 11087 | NULL | NULL | YES | BTREE | | |
| api_plexts | 1 | index_api_plexts_on_to_api_portal_id | 1 | to_api_portal_id | A | 9303 | NULL | NULL | YES | BTREE | | |
| api_plexts | 1 | testime | 1 | api_agent_id | A | 1817 | NULL | NULL | YES | BTREE | | |
| api_plexts | 1 | testime | 2 | from_api_portal_id | A | 52277 | NULL | NULL | YES | BTREE | | |
+------------+------------+----------------------------------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
7 rows in set (0.00 sec)
VOILA!!!
mysql> explain select count(*) from api_portals ap JOIN api_plexts al on ap.id = al.from_api_portal_id where al.api_agent_id = 2\G; *************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: al
partitions: NULL
type: ref
possible_keys: index_api_plexts_on_api_agent_id,index_api_plexts_on_from_api_portal_id,testime
key: testime
key_len: 5
ref: const
rows: 271
filtered: 100.00
Extra: Using where; Using index
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: ap
partitions: NULL
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 8
ref: ingress.al.from_api_portal_id
rows: 1
filtered: 100.00
Extra: Using where; Using index
2 rows in set, 1 warning (0.00 sec)
I have a table that stores tags in the JSON format for each page. Current table has about 1,000,000 records.
When I run
SELECT COUNT(*) FROM alltags WHERE tagtype = 'pages' AND JSON_SEARCH(tags, 'one', 'Hampden') IS NOT NULL;
the query takes about 20 seconds
+----------+
| COUNT(*) |
+----------+
| 23500 |
+----------+
1 row in set (19.20 sec)
The query is slow regardless if I use InnoDB or MyISAM engine.
I got indexes for tagtype, tags and tagtype with tags columns. I need the exact number of results for pagination. Is there a way I can speed up the SELECT COUNT query? Selecting actual data is pretty instant.
SELECT id FROM alltags WHERE tagtype = 'pages' AND JSON_SEARCH(tags, 'one', 'Hampden') IS NOT NULL LIMIT 0,10;
+------+
| id |
+------+
| 5072 |
| 5075 |
| 5078 |
| 5081 |
| 5084 |
| 5087 |
| 5090 |
| 5093 |
| 5096 |
| 5099 |
+------+
10 rows in set (0.01 sec)
These are the indexes
SHOW INDEX FROM alltags;
+---------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| alltags | 0 | PRIMARY | 1 | id | A | 896649 | NULL | NULL | | BTREE | | |
| alltags | 1 | tagtype | 1 | tagtype | A | 2 | NULL | NULL | | BTREE | | |
| alltags | 1 | keyword | 1 | keyword | A | 33 | NULL | NULL | | BTREE | | |
| alltags | 1 | tags | 1 | tags | A | 89665 | NULL | NULL | YES | BTREE | | |
+---------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
EXPLAIN results:
Select data
EXPLAIN SELECT id FROM alltags WHERE tagtype = 'pages' AND JSON_SEARCH(tags, 'one', 'Hampden') IS NOT NULL;
+----+-------------+---------+------------+------+---------------+---------+---------+-------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+---------+---------+-------+--------+----------+-------------+
| 1 | SIMPLE | alltags | NULL | ref | tagtype | tagtype | 7 | const | 892372 | 100.00 | Using where |
+----+-------------+---------+------------+------+---------------+---------+---------+-------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
Select count
EXPLAIN SELECT COUNT(*) FROM alltags WHERE tagtype = 'pages' AND JSON_SEARCH(tags, 'one', 'Hampden') IS NOT NULL;
+----+-------------+---------+------------+------+---------------+---------+---------+-------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+---------+---------+-------+--------+----------+-------------+
| 1 | SIMPLE | alltags | NULL | ref | tagtype | tagtype | 7 | const | 892372 | 100.00 | Using where |
+----+-------------+---------+------------+------+---------------+---------+---------+-------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
This question already has answers here:
SELECT vs UPDATE performance with index
(7 answers)
Closed 8 years ago.
I'm trying to improve my query so that it doesn't take so long. Is there anything I can try?
I'm using InnoDB.
My table:
mysql> describe hunted_place_review_external_urls;
+--------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| worker_id | varchar(255) | YES | MUL | NULL | |
| queued_at | bigint(20) | YES | MUL | NULL | |
| external_url | varchar(255) | NO | | NULL | |
| place_id | varchar(63) | NO | MUL | NULL | |
| source_id | varchar(63) | NO | | NULL | |
| successful | tinyint(1) | NO | | 0 | |
+--------------+--------------+------+-----+---------+----------------+
mysql> show index from hunted_place_review_external_urls;
+-----------------------------------+------------+--------------------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------------------------------+------------+--------------------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| hunted_place_review_external_urls | 0 | PRIMARY | 1 | id | A | 5118685 | NULL | NULL | | BTREE | | |
| hunted_place_review_external_urls | 1 | worker_id | 1 | worker_id | A | 5118685 | NULL | NULL | YES | BTREE | | |
| hunted_place_review_external_urls | 1 | queued_at | 1 | queued_at | A | 5118685 | NULL | NULL | YES | BTREE | | |
| hunted_place_review_external_urls | 1 | worker_id_and_queued_at | 1 | worker_id | A | 5118685 | NULL | NULL | YES | BTREE | | |
| hunted_place_review_external_urls | 1 | worker_id_and_queued_at | 2 | queued_at | A | 5118685 | NULL | NULL | YES | BTREE | | |
| hunted_place_review_external_urls | 1 | place_id_source_id_external_url_successful | 1 | place_id | A | 5118685 | NULL | NULL | | BTREE | | |
| hunted_place_review_external_urls | 1 | place_id_source_id_external_url_successful | 2 | source_id | A | 5118685 | NULL | NULL | | BTREE | | |
| hunted_place_review_external_urls | 1 | place_id_source_id_external_url_successful | 3 | external_url | A | 5118685 | NULL | NULL | | BTREE | | |
| hunted_place_review_external_urls | 1 | place_id_source_id_external_url_successful | 4 | successful | A | 5118685 | NULL | NULL | | BTREE | | |
+-----------------------------------+------------+--------------------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
My query:
mysql> select count(*) from hunted_place_review_external_urls;
+----------+
| count(*) |
+----------+
| 4217356 |
+----------+
1 row in set (0.96 sec)
mysql> select count(*) from hunted_place_review_external_urls where worker_id is null;
+----------+
| count(*) |
+----------+
| 772626 |
+----------+
1 row in set (0.27 sec)
mysql> update hunted_place_review_external_urls set worker_id = "123" where worker_id is null order by queued_at asc limit 1;
Query OK, 1 row affected (4.80 sec)
Rows matched: 1 Changed: 1 Warnings: 0
Why is the update query taking 4s even though I have both single and composite index on queued_at and worker_id? This never happened before when the number of rows with worker_id = null was much lower. With ~200k rows instead of 780k rows, it would only take a few milliseconds.
Note the equivalent query with SELECT instead of UPDATE is extremely fast:
mysql> select * from hunted_place_review_external_urls where worker_id is null order by queued_at asc limit 1;
1 row in set (0.00 sec)
My queued_at values are timestamps expressed in number of milliseconds, such as 1398210069531
I've tried dropping my single indices on worker_id and queued_at but the problem remains:
mysql> drop index queued_at on hunted_place_review_external_urls;
Query OK, 0 rows affected (3.75 sec)
mysql> drop index worker_id on hunted_place_review_external_urls;
Query OK, 0 rows affected (3.75 sec)
mysql> show index from hunted_place_review_external_urls;
+-----------------------------------+------------+--------------------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------------------------------+------------+--------------------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| hunted_place_review_external_urls | 0 | PRIMARY | 1 | id | A | 5118685 | NULL | NULL | | BTREE | | |
| hunted_place_review_external_urls | 1 | worker_id_and_queued_at | 1 | worker_id | A | 5118685 | NULL | NULL | YES | BTREE | | |
| hunted_place_review_external_urls | 1 | worker_id_and_queued_at | 2 | queued_at | A | 5118685 | NULL | NULL | YES | BTREE | | |
| hunted_place_review_external_urls | 1 | place_id_source_id_external_url_successful | 1 | place_id | A | 5118685 | NULL | NULL | | BTREE | | |
| hunted_place_review_external_urls | 1 | place_id_source_id_external_url_successful | 2 | source_id | A | 5118685 | NULL | NULL | | BTREE | | |
| hunted_place_review_external_urls | 1 | place_id_source_id_external_url_successful | 3 | external_url | A | 5118685 | NULL | NULL | | BTREE | | |
| hunted_place_review_external_urls | 1 | place_id_source_id_external_url_successful | 4 | successful | A | 5118685 | NULL | NULL | | BTREE | | |
+-----------------------------------+------------+--------------------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
Here's my EXPLAIN SELECT statement. I'm using an old version of MYSQL that doesn't support EXPLAIN UPDATE:
mysql> explain select * from hunted_place_review_external_urls where worker_id is null order by queued_at asc limit 1;
+----+-------------+-----------------------------------+------+-------------------------+-------------------------+---------+-------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------------------------------+------+-------------------------+-------------------------+---------+-------+---------+-------------+
| 1 | SIMPLE | hunted_place_review_external_urls | ref | worker_id_and_queued_at | worker_id_and_queued_at | 768 | const | 1587282 | Using where |
+----+-------------+-----------------------------------+------+-------------------------+-------------------------+---------+-------+---------+-------------+
1 row in set (0.00 sec)
This is your query:
update hunted_place_review_external_urls
set worker_id = "123"
where worker_id is null
order by queued_at asc
limit 1;
It first has to find the row use for updating, and that requires applying the where clause and the order by clause. Either it does all the work (scanning the table and then sorting), or it uses an index. The proper index would be hunted_place_review_external_urls(worker_id, queued_at). You can add more columns at the end, but these must be the first two columns, and in that order.
EDIT:
Given that the select is fast, try this version:
update hunted_place_review_external_urls toupdate join
(select
from hunted_place_review_external_urls
where worker_id is null
order by queued_at asc
limit 1
) l
on toupdate.id = l.id
set toupdate.worker_id = '123';
I'm not sure why the indexes would be used properly here but not in the update, but hopefully this will work.
Compare the Time of Drop Index, Create Index and update.
You may notice a correlation.
When you are simple performing SELECT Queries, indexes are USEFUL, cause they speed things up.
When you are performing UPDATE or DELETE Statements - Indexes are bad, cause they slow things down! Whenever you change a value of an indexed column, MySQL needs to rebuild the index for any subsequent row. (assuming you are ALWAYS fetching the oldest entry - that means: Reindex ALL reamaining 772625 Rows.)
Try to drop the index on worker_id and see the Update Performance. If worker_id is not indexed, the update will be way faster. (Finding the entry to update is still as fast as before, because it mainly depends on the sorting performed on the indexed column queued_at and a very small subset of unindexed null values on the worker_id, matching the desired queued_at value)
I just created a dummy db and tested your setup:
With 1.000.000 Rows and both - the single index on worker_id and the composite index on worker_id|queued at, a select looks like:
SELECT * FROM `tbl` WHERE ISNULL( worker_ID ) ORDER BY queued_at ASC LIMIT 1
and the performance:
Query took 0.3360 sec
Trying to modify the worker_id in the way you do, results in:
UPDATE `tbl` SET worker_id=1 WHERE ISNULL(worker_ID) ORDER BY queued_at ASC LIMIT 1
performance:
1 row affected. (Query took 7.9592 sec)
Droping both the indexes (single and composite) on worker_id, then the same query results in:
1 row affected. (Query took 1.4364 sec)
(generated 50.000 rows per insert, so they have the same date, so the index is not "perfect" for searching, so "real" data may perform way better.)
I have the following InnoDB table, which acts kinda weird under MySQL 5.1.49
(mysql Ver 14.14 Distrib 5.1.49, for debian-linux-gnu (x86_64) using readline 6.1)
mysql> desc forum_favorite;
+-----------+----------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+----------------------+------+-----+---------+-------+
| id_member | smallint(5) unsigned | YES | MUL | NULL | |
| id_topic | int(10) unsigned | YES | MUL | NULL | |
+-----------+----------------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
mysql> show index from forum_favorite;
+----------------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+----------------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| forum_favorite | 1 | id_member | 1 | id_member | A | 2134 | NULL | NULL | YES | BTREE | |
| forum_favorite | 1 | id_topic | 1 | id_topic | A | 3201 | NULL | NULL | YES | BTREE | |
+----------------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
2 rows in set (0.00 sec)
Now, check the query:
mysql> SELECT id_topic FROM forum_favorite WHERE (id_member = 2);
+----------+
| id_topic |
+----------+
| 1249 |
| 20209 |
| 91878 |
| 99026 |
| 90257 |
| 1179 |
| 1179 |
+----------+
7 rows in set (0.00 sec)
When I search for a specific topic with a given member, it gives an empty result set. WHY?
mysql> select * from forum_favorite where id_member = 2 and id_topic = 1249;
Empty set (0.00 sec)
But when I search for another topic, it comes back ok...
It can be found without id_member in the where clause:
mysql> select * from forum_favorite where id_topic = 1249;
+-----------+----------+
| id_member | id_topic |
+-----------+----------+
| 2 | 1249 |
+-----------+----------+
1 rows in set (0.00 sec)
Indexes:
mysql> show index from forum_favorite;
+----------------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+----------------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| forum_favorite | 1 | id_member | 1 | id_member | A | 2134 | NULL | NULL | YES | BTREE | |
| forum_favorite | 1 | id_topic | 1 | id_topic | A | 3201 | NULL | NULL | YES | BTREE | |
+----------------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
2 rows in set (0.00 sec)
Explain on the incriminating query:
mysql> explain select * from forum_favorite where id_member = 2 and id_topic = 1249;
+----+-------------+----------------+-------------+--------------------+--------------------+---------+------+------+---------------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------------+-------------+--------------------+--------------------+---------+------+------+---------------------------------------------------------------+
| 1 | SIMPLE | forum_favorite | index_merge | id_member,id_topic | id_member,id_topic | 3,5 | NULL | 1 | Using intersect(id_member,id_topic); Using where; Using index |
+----+-------------+----------------+-------------+--------------------+--------------------+---------+------+------+---------------------------------------------------------------+
1 row in set (0.00 sec)
Explain on the query, with another topicid. WHAT THE HELL???
mysql> explain select * from forum_favorite where (id_member = 2) and id_topic = 20209;
+----+-------------+----------------+------+--------------------+----------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------------+------+--------------------+----------+---------+-------+------+-------------+
| 1 | SIMPLE | forum_favorite | ref | id_member,id_topic | id_topic | 5 | const | 1 | Using where |
+----+-------------+----------------+------+--------------------+----------+---------+-------+------+-------------+
1 row in set (0.00 sec
I have migrated from an old mysql 4.x database to mysql 5.1 recently, where the above queries gave consistent results.
What could be the problem?!?! What makes the optimizer go bonkers?
Have you tried to run OPTIMIZE on the table?
UPD:
For InnoDB tables, OPTIMIZE TABLE is mapped to ALTER TABLE, which rebuilds the table to update index statistics and free unused space in the clustered index.
I think the problem is actually in Using intersect(id_member,id_topic);. I would extend the id_topic index to (id_topic, id_member) so that it used a single key for the lookup instead of index merge (which is actually rather rare in MySQL).
I found a very weird mysql behaviour : when I run a specific query twice, the explain of this query is different the second time :
query = SELECT `twstats_twwordstrend`.`id`, `twstats_twwordstrend`.`created`, `twstats_twwordstrend`.`freq`, `twstats_twwordstrend`.`word_id` FROM `twstats_twwordstrend` INNER JOIN `twstats_twwords` ON (`twstats_twwordstrend`.`word_id` = `twstats_twwords`.`id`) WHERE (`twstats_twwords`.`name` = '#ladygaga' AND `twstats_twwordstrend`.`created` > '2011-01-28 01:30:19' );
1st query execution and then run explain :
mysql> EXPLAIN SELECT `twstats_twwordstrend`.`id`, `twstats_twwordstrend`.`created`, `twstats_twwordstrend`.`freq`, `twstats_twwordstrend`.`word_id` FROM `twstats_twwordstrend` INNER JOIN `twstats_twwords` ON (`twstats_twwordstrend`.`word_id` = `twstats_twwords`.`id`) WHERE (`twstats_twwords`.`name` = '#ladygaga' AND `twstats_twwordstrend`.`created` > '2011-01-28 01:30:19' );
+----+-------------+----------------------+--------+-------------------------------+---------+---------+-------------------------------------------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------------------+--------+-------------------------------+---------+---------+-------------------------------------------+---------+-------------+
| 1 | SIMPLE | twstats_twwordstrend | ALL | twstats_twwordstrend_4b95d890 | NULL | NULL | NULL | 4877401 | Using where |
| 1 | SIMPLE | twstats_twwords | eq_ref | PRIMARY | PRIMARY | 4 | statweestics.twstats_twwordstrend.word_id | 1 | Using where |
+----+-------------+----------------------+--------+-------------------------------+---------+---------+-------------------------------------------+---------+-------------+
2 rows in set (0.00 sec)
2nd query execution and then run explain :
mysql> EXPLAIN SELECT `twstats_twwordstrend`.`id`, `twstats_twwordstrend`.`created`, `twstats_twwordstrend`.`freq`, `twstats_twwordstrend`.`word_id` FROM `twstats_twwordstrend` INNER JOIN `twstats_twwords` ON (`twstats_twwordstrend`.`word_id` = `twstats_twwords`.`id`) WHERE (`twstats_twwords`.`name` = '#ladygaga' AND `twstats_twwordstrend`.`created` > '2011-01-28 01:30:19' );
+----+-------------+----------------------+------+-------------------------------+-------------------------------+---------+---------------------------------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------------------+------+-------------------------------+-------------------------------+---------+---------------------------------+--------+-------------+
| 1 | SIMPLE | twstats_twwords | ALL | PRIMARY | NULL | NULL | NULL | 222994 | Using where |
| 1 | SIMPLE | twstats_twwordstrend | ref | twstats_twwordstrend_4b95d890 | twstats_twwordstrend_4b95d890 | 4 | statweestics.twstats_twwords.id | 15 | Using where |
+----+-------------+----------------------+------+-------------------------------+-------------------------------+---------+---------------------------------+--------+-------------+
2 rows in set (0.00 sec)
mysql> describe twstats_twwords;
+---------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| created | datetime | NO | | NULL | |
| name | varchar(140) | NO | | NULL | |
+---------+--------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
mysql> describe twstats_twwordstrend;
+---------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+----------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| created | datetime | NO | | NULL | |
| freq | double | NO | | NULL | |
| word_id | int(11) | NO | MUL | NULL | |
+---------+----------+------+-----+---------+----------------+
4 rows in set (0.00 sec)
How this can be possible ??
Look at the rows column. The engine was able to gather more statistics -- so the next time it will try to use the better plan.
Happy coding.