MariaDB Logic Change with very large IN (...) set - mysql

I have a particular MariaDB Query that utilizes some joins and some IN ('...') conditions.
Generally it returns results in < 2sec on large data sets (~50M records), however when a very large number of options are presented in the IN condition, (Eg. 1000+ IN options) the query takes 5+ hours and the logic completely changes when running an ANALYZE on the queries.
Looking to understand why this is the case and suggestions on how I might be able to resolve the bottleneck. Presently thinking the simplest option may be to drop the IN condition completely and filter the results in PHP instead of SQL, as if the IN condition is dropped results are returned in <1s on same tables.
ANALYZE results from query where small IN set used.
+------+-------------+-------+-------+------------------------+----------+---------+--------------------+-------+---------+----------+------------+---------------------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | r_rows | filtered | r_filtered | Extra |
+------+-------------+-------+-------+------------------------+----------+---------+--------------------+-------+---------+----------+------------+---------------------------------------------------------------------+
| 1 | SIMPLE | t1 | range | calldate,call_id | calldate | 7 | NULL | 13400 | 7162.00 | 100.00 | 33.01 | Using index condition; Using where; Using temporary; Using filesort |
| 1 | SIMPLE | t2 | ref | PRIMARY,called,call_id | call_id | 4 | crimson.t1.call_id | 1 | 3.21 | 100.00 | 35.24 | Using where |
| 1 | SIMPLE | d1 | ref | digits,leg_id | leg_id | 4 | crimson.t2.xid | 1 | 1.94 | 100.00 | 0.58 | Using index condition; Using where |
| 1 | SIMPLE | g1 | ref | call_id | call_id | 4 | crimson.t1.call_id | 1 | 3.00 | 100.00 | 0.00 | Using where |
+------+-------------+-------+-------+------------------------+----------+---------+--------------------+-------+---------+----------+------------+---------------------------------------------------------------------+
4 rows in set (1.154 sec)
ANALYZE results from the same query and conditions where 1200 IN set has been used.
+------+--------------+-------------+--------+------------------------+---------+---------+--------------------+------+---------+----------+------------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | r_rows | filtered | r_filtered | Extra |
+------+--------------+-------------+--------+------------------------+---------+---------+--------------------+------+---------+----------+------------+---------------------------------+
| 1 | PRIMARY | <subquery2> | ALL | distinct_key | NULL | NULL | NULL | 1222 | 1222.00 | 100.00 | 100.00 | Using temporary; Using filesort |
| 1 | PRIMARY | d1 | ref | digits,leg_id | digits | 29 | tvc_0._col_1 | 5 | 6192.72 | 100.00 | 100.00 | Using index condition |
| 1 | PRIMARY | t2 | eq_ref | PRIMARY,called,call_id | PRIMARY | 8 | crimson.d1.leg_id | 1 | 1.00 | 100.00 | 36.73 | Using where |
| 1 | PRIMARY | g1 | ref | call_id | call_id | 4 | crimson.t2.call_id | 1 | 3.32 | 100.00 | 0.05 | Using where |
| 1 | PRIMARY | t1 | ref | calldate,call_id | call_id | 4 | crimson.t2.call_id | 1 | 5.19 | 100.00 | 0.03 | Using where |
| 2 | MATERIALIZED | <derived3> | ALL | NULL | NULL | NULL | NULL | 1222 | 1222.00 | 100.00 | 100.00 | |
| 3 | DERIVED | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
+------+--------------+-------------+--------+------------------------+---------+---------+--------------------+------+---------+----------+------------+---------------------------------+
7 rows in set (5 hours 16 min 16.738 sec)
ANALYZE without any IN condition at all.
+------+-------------+-------+-------+------------------------+----------+---------+--------------------+-------+---------+----------+------------+---------------------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | r_rows | filtered | r_filtered | Extra |
+------+-------------+-------+-------+------------------------+----------+---------+--------------------+-------+---------+----------+------------+---------------------------------------------------------------------+
| 1 | SIMPLE | t1 | range | calldate,call_id | calldate | 7 | NULL | 13400 | 7162.00 | 100.00 | 33.01 | Using index condition; Using where; Using temporary; Using filesort |
| 1 | SIMPLE | t2 | ref | PRIMARY,called,call_id | call_id | 4 | crimson.t1.call_id | 1 | 3.21 | 100.00 | 35.24 | Using where |
| 1 | SIMPLE | g1 | ref | call_id | call_id | 4 | crimson.t1.call_id | 1 | 3.57 | 100.00 | 0.06 | Using where |
| 1 | SIMPLE | d1 | ref | leg_id | leg_id | 4 | crimson.t2.xid | 1 | 1.33 | 100.00 | 100.00 | Using index condition |
+------+-------------+-------+-------+------------------------+----------+---------+--------------------+-------+---------+----------+------------+---------------------------------------------------------------------+
4 rows in set (0.093 sec)
Example Tables:
CREATE TABLE `digit_dial_map_x` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`leg_id` int(11) NOT NULL,
`sequence` int(2) NOT NULL,
`digits` varchar(26) DEFAULT NULL,
`category` varchar(2) NOT NULL DEFAULT 'M',
INDEX `leg_id` (`leg_id`),
INDEX `digits` (`digits`),
INDEX `category` (`category`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `call_legs_x` (
`xid` bigint(20) NOT NULL AUTO_INCREMENT,
`call_id` int(11) NOT NULL,
`calldate` date NOT NULL,
`start_time` time DEFAULT NULL,
`duration_hr` varchar(6) DEFAULT NULL,
`duration_min` varchar(2) DEFAULT NULL,
`duration_sec` varchar(2) DEFAULT NULL,
`calling` varchar(7) DEFAULT NULL,
`called` varchar(7) DEFAULT NULL,
`ans` varchar(2) DEFAULT NULL,
`ans_time` varchar(4) DEFAULT NULL,
`digits_dialed` varchar(26) DEFAULT NULL,
`digits_actual` varchar(26) DEFAULT NULL,
`ani` varchar(20) DEFAULT NULL,
`dnis` varchar(10) DEFAULT NULL,
`extn` varchar(10) DEFAULT NULL,
`trans_conf` varchar(2) DEFAULT NULL,
`third_party` varchar(7) DEFAULT NULL,
`sysid` varchar(3) DEFAULT NULL,
`call_log_id` varchar(12) DEFAULT NULL,
`assoc_log_id` varchar(12) DEFAULT NULL,
`raw_id` int(11) NOT NULL,
`leg` varchar(2) DEFAULT NULL,
`call_start` datetime DEFAULT NULL,
`call_end` datetime DEFAULT NULL,
`call_start_utc` datetime DEFAULT NULL,
`call_end_utc` datetime DEFAULT NULL,
INDEX `calldate` (`calldate`, `start_time`),
INDEX `called` (`called`),
INDEX `call_id` (`call_id`),
INDEX `digits_dialed` (`digits_dialed`),
INDEX `raw_id` (`raw_id`),
INDEX `call_start` (`call_start`),
INDEX `call_end` (`call_end`),
INDEX `call_start_utc` (`call_start`),
INDEX `call_end_utc` (`call_end`),
INDEX `calling` (`calling`),
INDEX `ans_time` (`ans_time`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Example Query (omitted the 1200 IN options):
SELECT t1.call_id, t2.ans_time, t2.ans, ((t2.duration_hr * 3600) + (t2.duration_min *60) + t2.duration_sec) as duration, t2.digits_dialed, t2.digits_actual, t2.dnis, t2.trans_conf, t1.ani, t1.calling, t2.called, d1.digits, g1.extn
FROM call_legs_55 as t1
JOIN call_legs_55 as t2 ON t1.call_id=t2.call_id
JOIN digit_dial_map_55 as d1 ON t2.xid=d1.leg_id
JOIN call_legs_55 as g1 ON t1.call_id=g1.call_id
WHERE (t1.calldate BETWEEN '2019-11-25' AND '2019-11-25') AND NOT ((t1.calldate = '2019-11-25') AND (t1.start_time < '00:00:00')) AND NOT((t1.calldate = '2019-11-25') AND (t1.start_time > '24:00:00'))
AND (t1.calling IN ('T6001','T6002') )
AND d1.digits IN ('...')
AND t2.called !='X9999'
AND t1.calling != 'X9999'
AND t1.calling != ''
AND t2.ans_time != ''
AND (g1.extn IN ('52043','52042','52132','52116') AND g1.extn != t1.calling)
GROUP BY CONCAT(t1.call_id, g1.extn);

digit_dial_map_x is a many:many mapping table, correct?? It's indexes are very inefficient.
Recommend:
PRIMARY KEY(leg_id, digits),
INDEX(digits, leg_id)
More discussion: http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table
(The table did not happen to be such.)

Related

Same SQL query taking much more time in a different server

I've recently moved a site to a different server, and while the overall performance is better, there's one specific SQL query that's taking about 5 seconds to execute now, while it only takes 0.1 seconds on the old server.
Query:
SELECT t1.*
FROM wp_ap_activity t1 NATURAL JOIN (SELECT max(activity_date) AS activity_date
FROM wp_ap_activity
WHERE activity_q_id IN(126187,125933,126043,126083,100007,125781,125628,125615,125716,125728,126115,126061,126028,125429,124783,125651,126092,125510,126062,126058,125923,125727,125948,125085,126033,125975,125537,124664,126031,125947,125938,123327,125908,125467,125471,125852,125558,125980,125226,125904,124454,103489,125935,125925,124472,122940,125949,125950,125139,112744,124718,124626,125859,125903,125406,66537,125722,125887,125810,124810,125782,125823,125799,108626,99836,85975,74147,69962,69510,68598,68593,125875,125620,92246,112851,108528,108629,112864,106120,119571,125798,118205,125831,108547,125550,125813,124297,125223,125792,125536,125730,123848,125411,125598,125638,125698,125519,125700,125697,125151,125688,125445,125715,125083,125669,125665,125673,124777,123975,125528,125724,125146,125610,124784,125617,125631,125637,124765,125496,125647,125571,125245,125264,125513,125534,124854,125527,125543,125535,125515,125337,125221,125202,125549,125530,125531,125541,124952,125358,125502,125427,125525,125123,125361,125252,125421,125491,125263,125260,124743)
GROUP BY activity_q_id) t2
ORDER BY t2.activity_date
New Server
MySQL Version: 10.3.16-MariaDB-1:10.3.16+maria~jessie
Execution Time: 5.2812 seconds
Table Name: wp_ap_activity
Table Rows: 109,947
Space Usage:
Data: 9.5 MiB
Index: 10.5 MiB
Effective: 20.1 MiB
Total: 20.1 MiB
SHOW CREATE TABLE wp_ap_activity results:
CREATE TABLE `wp_ap_activity` (
`activity_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`activity_action` varchar(45) NOT NULL,
`activity_q_id` bigint(20) unsigned NOT NULL,
`activity_a_id` bigint(20) unsigned DEFAULT NULL,
`activity_c_id` bigint(20) unsigned DEFAULT NULL,
`activity_user_id` bigint(20) unsigned NOT NULL,
`activity_date` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`activity_id`),
KEY `activity_q_id` (`activity_q_id`),
KEY `activity_a_id` (`activity_a_id`),
KEY `activity_user_id` (`activity_user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=113859 DEFAULT CHARSET=utf8mb4
Old Server
MySQL Version: 5.7.26-0ubuntu0.16.04.1
Execution Time: 0.1842 seconds
Table Name: wp_ap_activity
Table Rows: 109,759
Space Usage:
Data: 1.5 MiB
Index: 4.5 MiB
Total: 6 MiB
SHOW CREATE TABLE wp_ap_activity results:
CREATE TABLE `wp_ap_activity` (
`activity_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`activity_action` varchar(45) NOT NULL,
`activity_q_id` bigint(20) unsigned NOT NULL,
`activity_a_id` bigint(20) unsigned DEFAULT NULL,
`activity_c_id` bigint(20) unsigned DEFAULT NULL,
`activity_user_id` bigint(20) unsigned NOT NULL,
`activity_date` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`activity_id`),
KEY `activity_q_id` (`activity_q_id`),
KEY `activity_a_id` (`activity_a_id`),
KEY `activity_user_id` (`activity_user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=113657 DEFAULT CHARSET=utf8mb4
The table structure, primary keys, indexes are identical.
The new table reports it's total size as 20.1 MiB while the old one is much smaller at 6 MiB - I'm not sure why this is happening and if it has to do with the slow performance.
Both tables are InnoDB and have collation set to utf8mb4_general_ci
Any advice is greatly appreciated.
Explain query:
New Server (performs slow)
+----+-------------+----------------+-------+---------------+---------------+---------+------+--------+-------------------------------------------------+--+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | |
+----+-------------+----------------+-------+---------------+---------------+---------+------+--------+-------------------------------------------------+--+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 970 | Using temporary; Using filesort | |
+----+-------------+----------------+-------+---------------+---------------+---------+------+--------+-------------------------------------------------+--+
| 1 | PRIMARY | t1 | ALL | NULL | NULL | NULL | NULL | 109514 | Using where; Using join buffer (flat, BNL join) | |
+----+-------------+----------------+-------+---------------+---------------+---------+------+--------+-------------------------------------------------+--+
| 2 | DERIVED | wp_ap_activity | range | activity_q_id | activity_q_id | 8 | NULL | 970 | Using index condition | |
+----+-------------+----------------+-------+---------------+---------------+---------+------+--------+-------------------------------------------------+--+
Old Server (performs fast)
+----+-------------+----------------+------------+-------+---------------+---------------+---------+---------------------------+-------+----------+----------------------------------------------+--+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | |
+----+-------------+----------------+------------+-------+---------------+---------------+---------+---------------------------+-------+----------+----------------------------------------------+--+
| 1 | PRIMARY | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 20270 | 100.00 | Using where; Using temporary; Using filesort | |
+----+-------------+----------------+------------+-------+---------------+---------------+---------+---------------------------+-------+----------+----------------------------------------------+--+
| 1 | PRIMARY | <derived2> | NULL | ref | <auto_key0> | <auto_key0> | 5 | helpdesk.t1.activity_date | 10 | 100.00 | Using index | |
+----+-------------+----------------+------------+-------+---------------+---------------+---------+---------------------------+-------+----------+----------------------------------------------+--+
| 2 | DERIVED | wp_ap_activity | NULL | range | activity_q_id | activity_q_id | 8 | NULL | 970 | 100.00 | Using index condition | |
+----+-------------+----------------+------------+-------+---------------+---------------+---------+---------------------------+-------+----------+----------------------------------------------+--+
Updated EXPLAIN after applying the fix provided by Wilson Hauck. Query speed down to ~0.005 seconds!
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
|----|-------------|----------------|-------|---------------|---------------|---------|------------------|------|-----------------------------|
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 966 | Using where; Using filesort |
| 1 | PRIMARY | t1 | ref | activity_date | activity_date | 5 | t2.activity_date | 1 | |
| 2 | DERIVED | wp_ap_activity | range | activity_q_id | activity_q_id | 8 | NULL | 966 | Using index condition |
Your EXPLAIN's are nowhere close to being identical, look at end of lines. Second query has BNL in remarks, meaning Block Nested Loop processing (that is always SLOW). To be avoided.
You NEED an index on activity_date on EACH table.
Run from MySQL command prompt, SHOW INDEX FROM wp_ap_activity; on EACH server will rebuild the indexes so they are current.
Change queries to SELECT SQL_NO_CACHE ......... for testing to avoid using Query Cache results and get your timings again from the SECOND and THIRD execution of each query to compare.
Let us know your results, please.

How to find the right indexes for several joined tables

I have the following query. the main tables are company_reports and project_con_messages with 770,000 and 1,040,000 records respectively.
When I run this query it takes about 20 seconds and when I remove the last table company_par_user_settings it takes less than 0.5 seconds.
company_par_user_settings (short name: pus) has about 200,000 records and is meant to show user settings for each company_partner. on company_par_user_settings table we have composite unique index key on partner_id and user_id fields. I also removed the index and replaced with simple indexes on partner_id and user_id but at the end, it didn't make any big difference in running time.
SELECT *
FROM company_reports rep
LEFT JOIN system_users usr ON rep.user_id=usr.id
LEFT JOIN company_rep_subjects sbj ON rep.subject_id=sbj.id
INNER JOIN company_partners par ON rep.partner_id=par.id
LEFT JOIN project_con_messages mes ON rep.message_id=mes.id
LEFT JOIN company_par_user_settings pus ON par.id=pus.partner_id AND 1=pus.user_id
WHERE 1=1
ORDER BY rep.id DESC
LIMIT 0,50
Here below I added the explain on the above query:
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
| 1 | SIMPLE | rep | NULL | ALL | partner_id | NULL | NULL | NULL | 772236 | 100.00 | Using temporary; Using filesort |
| 1 | SIMPLE | par | NULL | eq_ref | PRIMARY | PRIMARY | 3 | portal_ebrahim.rep.partner_id | 1 | 100.00 | NULL |
| 1 | SIMPLE | usr | NULL | eq_ref | PRIMARY | PRIMARY | 2 | portal_ebrahim.rep.user_id | 1 | 100.00 | Using where |
| 1 | SIMPLE | sbj | NULL | eq_ref | PRIMARY | PRIMARY | 2 | portal_ebrahim.rep.subject_id | 1 | 100.00 | NULL |
| 1 | SIMPLE | mes | NULL | eq_ref | PRIMARY | PRIMARY | 4 | portal_ebrahim.rep.message_id | 1 | 100.00 | NULL |
| 1 | SIMPLE | pus | NULL | ALL | NULL | NULL | NULL | NULL | 191643 | 100.00 | Using where; Using join buffer (Block Nested Loop) |
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
I appreciate if someone help me on the right indexes or any other solutions that make the query run faster.
Edit:
Here is the show table for company_par_user_settings
CREATE TABLE `company_par_user_settings` (
`id` mediumint(9) NOT NULL AUTO_INCREMENT,
`partner_id` mediumint(8) unsigned NOT NULL,
`user_id` smallint(5) unsigned NOT NULL,
`access` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '0-Not specified',
`access_category` tinyint(1) unsigned NOT NULL DEFAULT '0',
`notify` tinyint(1) unsigned NOT NULL DEFAULT '0',
`stars` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `partner_id` (`partner_id`,`user_id`),
KEY `stars` (`stars`)
) ENGINE=MyISAM AUTO_INCREMENT=198729 DEFAULT CHARSET=utf8 COLLATE=utf8_persian_ci

Trouble with subquery but only in view

I have some trouble with the following query:
DROP VIEW IF EXISTS own_inst_stakes_detail_all;
CREATE VIEW `own_inst_stakes_detail_all` AS
SELECT
`own_inst_stakes_detail`.`FactSet_Entity_ID` AS `factset_entity_id`,
`own_inst_stakes_detail`.`FSYM_ID` AS `fsym_id`,
`own_inst_stakes_detail`.`as_of_date` AS `report_date`,
`own_inst_stakes_detail`.`Position` AS `adj_holding`,
`own_inst_stakes_detail`.`Position` / (SELECT own_sec_prices.adj_shares_outstanding FROM own_sec_prices WHERE own_sec_prices.`FSYM_ID` = own_inst_stakes_detail.`FSYM_ID` ORDER BY ABS(DATEDIFF(own_sec_prices.price_date, `own_inst_stakes_detail`.`as_of_date`)) DESC LIMIT 1) AS `adj_ratio`,
`own_inst_stakes_detail`.`Position` AS `reported_holding`,
`sym_coverage`.`Proper_Name` AS `security_proper_name`,
`sym_entity`.`Entity_Proper_Name` AS `entity_proper_name`
FROM
`own_inst_stakes_detail`
LEFT JOIN `sym_entity` ON `sym_entity`.`FactSet_Entity_ID` = `own_inst_stakes_detail`.`FactSet_Entity_ID`
LEFT JOIN `sym_coverage` ON `sym_coverage`.`FSYM_ID` = `own_inst_stakes_detail`.`FSYM_ID`;
It uses the following tables, which are of particular interest:
CREATE TABLE `own_inst_stakes_detail` (
`FactSet_Entity_ID` CHAR(8) NOT NULL,
`FSYM_ID` CHAR(8) NOT NULL,
`as_of_date` DATE NOT NULL,
`Position` DOUBLE DEFAULT NULL,
PRIMARY KEY (`FactSet_Entity_ID`,`FSYM_ID`,`as_of_date`),
KEY `idx_own_inst_stakes_detail_FactSet_Entity_ID` (`FactSet_Entity_ID`),
KEY `idx_own_inst_stakes_detail_FSYM_ID` (`FSYM_ID`),
KEY `idx_own_inst_stakes_detail_as_of_date` (`as_of_date`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
CREATE TABLE `own_sec_prices` (
`FSYM_ID` CHAR(8) NOT NULL,
`price_date` DATE NOT NULL,
`unadj_shares_outstanding` DOUBLE NOT NULL,
`adj_shares_outstanding` DOUBLE NOT NULL,
PRIMARY KEY (`FSYM_ID`,`price_date`),
KEY `idx_own_sec_prices_FSYM_ID` (`FSYM_ID`),
KEY `idx_own_sec_prices_price_date` (`price_date`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
The query in the view works really well running just the query but it is a catastrophe if that is wrapped in a view. The query never completes, probably because of a full table scan. The tables are really big which I am using here.
I have tried that with MySQL Server 5.7 (btw., with MariaDB 10.1 there are no problems). Is there a way to improve that?
The EXPLAINs look as follows (Maria and My):
MariaDB [factset]> explain SELECT * FROM own_inst_stakes_detail_all WHERE FSYM_ID = 'WK13LJ-S' ORDER BY report_date DESC, adj_ratio DESC;
+------+--------------------+------------------------+--------+------------------------------------+------------------------------------+---------+--------------------------------------------------+------+----------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+--------------------+------------------------+--------+------------------------------------+------------------------------------+---------+--------------------------------------------------+------+----------------------------------------------------+
| 1 | PRIMARY | own_inst_stakes_detail | ref | idx_own_inst_stakes_detail_FSYM_ID | idx_own_inst_stakes_detail_FSYM_ID | 8 | const | 8 | Using index condition; Using where; Using filesort |
| 1 | PRIMARY | sym_entity | eq_ref | PRIMARY | PRIMARY | 8 | factset.own_inst_stakes_detail.FactSet_Entity_ID | 1 | |
| 1 | PRIMARY | sym_coverage | const | PRIMARY | PRIMARY | 8 | const | 1 | Using where |
| 3 | DEPENDENT SUBQUERY | own_sec_prices | ref | PRIMARY,idx_own_sec_prices_FSYM_ID | idx_own_sec_prices_FSYM_ID | 8 | factset.own_inst_stakes_detail.FSYM_ID | 10 | Using temporary; Using filesort |
+------+--------------------+------------------------+--------+------------------------------------+------------------------------------+---------+--------------------------------------------------+------+----------------------------------------------------+
4 rows in set (0.01 sec)
mysql> explain SELECT * FROM own_inst_stakes_detail_all WHERE FSYM_ID = 'WK13LJ-S' ORDER BY report_date DESC, adj_ratio DESC;
+----+--------------------+------------------------+------------+--------+------------------------------------+----------------------------+---------+--------------------------------------------------+--------+----------+---------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------------+------------------------+------------+--------+------------------------------------+----------------------------+---------+--------------------------------------------------+--------+----------+---------------------------------+
| 1 | PRIMARY | <derived2> | NULL | ref | <auto_key0> | <auto_key0> | 8 | const | 10 | 100.00 | Using where; Using filesort |
| 2 | DERIVED | own_inst_stakes_detail | NULL | ALL | NULL | NULL | NULL | NULL | 817961 | 100.00 | NULL |
| 2 | DERIVED | sym_entity | NULL | eq_ref | PRIMARY | PRIMARY | 8 | factset.own_inst_stakes_detail.FactSet_Entity_ID | 1 | 100.00 | NULL |
| 2 | DERIVED | sym_coverage | NULL | eq_ref | PRIMARY | PRIMARY | 8 | factset.own_inst_stakes_detail.FSYM_ID | 1 | 100.00 | NULL |
| 3 | DEPENDENT SUBQUERY | own_sec_prices | NULL | ref | PRIMARY,idx_own_sec_prices_FSYM_ID | idx_own_sec_prices_FSYM_ID | 8 | factset.own_inst_stakes_detail.FSYM_ID | 10 | 100.00 | Using temporary; Using filesort |
+----+--------------------+------------------------+------------+--------+------------------------------------+----------------------------+---------+--------------------------------------------------+--------+----------+---------------------------------+
5 rows in set, 3 warnings (0.08 sec)
To me it looks like MySQL cannot use the index idx_own_inst_stakes_detail_FSYM_ID. But if I change the column with the subquery to "1 as adj_ratio", the following happens:
mysql> explain SELECT * FROM own_inst_stakes_detail_all WHERE FSYM_ID = 'WK13LJ-S';
+----+-------------+------------------------+------------+--------+------------------------------------+------------------------------------+---------+--------------------------------------------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------------------+------------+--------+------------------------------------+------------------------------------+---------+--------------------------------------------------+------+----------+-------+
| 1 | SIMPLE | own_inst_stakes_detail | NULL | ref | idx_own_inst_stakes_detail_FSYM_ID | idx_own_inst_stakes_detail_FSYM_ID | 8 | const | 8 | 100.00 | NULL |
| 1 | SIMPLE | sym_entity | NULL | eq_ref | PRIMARY | PRIMARY | 8 | factset.own_inst_stakes_detail.FactSet_Entity_ID | 1 | 100.00 | NULL |
| 1 | SIMPLE | sym_coverage | NULL | const | PRIMARY | PRIMARY | 8 | const | 1 | 100.00 | NULL |
+----+-------------+------------------------+------------+--------+------------------------------------+------------------------------------+---------+--------------------------------------------------+------+----------+-------+
3 rows in set, 1 warning (0.00 sec)
That works well! Any help is greatly appreciated. Thanks!

Mysql is not using index on query

here is my explain result.
+----+-------------+----------------+--------+-------------------------------------------------------+-------------+---------+-------------------------------------+------+----------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------------+--------+-------------------------------------------------------+-------------+---------+-------------------------------------+------+----------------------------------------------------+
| 1 | SIMPLE | client_address | ALL | client_id | NULL | NULL | NULL | 1619 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | clients | eq_ref | PRIMARY,contract_id,contract_id_2 | PRIMARY | 3 | defenselaw.client_address.client_id | 1 | Using where |
| 1 | SIMPLE | user_infos | eq_ref | user_id | user_id | 3 | defenselaw.clients.client_rep_id | 1 | Using where |
| 1 | SIMPLE | users | eq_ref | PRIMARY | PRIMARY | 2 | defenselaw.user_infos.user_id | 1 | Using where; Using index |
| 1 | SIMPLE | contracts | eq_ref | PRIMARY | PRIMARY | 3 | defenselaw.clients.contract_id | 1 | NULL |
| 1 | SIMPLE | documents | eq_ref | contract_id,contract_id_2,contract_id_3,contract_id_4 | contract_id | 3 | defenselaw.clients.contract_id | 1 | Using where |
| 1 | SIMPLE | client_info | ref | client_id,client_id_2,client_id_3,index5 | client_id | 3 | defenselaw.client_address.client_id | 1 | Using where |
| 1 | SIMPLE | location | ALL | PRIMARY | NULL | NULL | NULL | 1 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+----------------+--------+-------------------------------------------------------+-------------+---------+-------------------------------------+------+----------------------------------------------------+
here is the query.
SELECT
contracts.contract_id,
documents.documentKey,
documents.document_name,
documents.notify_accountant,
clients.client_first_name,
clients.client_middle_name,
clients.client_last_name,
clients.client_rep_id,
client_address.client_street,
client_address.client_apartment,
client_address.client_city,
client_address.client_state,
client_address.client_zip,
client_info.client_email,
client_info.client_home_phone,
client_info.client_work_phone,
client_info.client_cell_phone,
client_info.client_fax_number,
contracts.contract_agreement_date,
user_infos.user_first_name,
user_infos.user_last_name,
location.location
FROM
`contracts`
LEFT JOIN
`clients`
ON clients.contract_id = contracts.contract_id
INNER JOIN
`client_address`
ON client_address.client_id = clients.client_id
INNER JOIN
`client_info`
ON client_info.client_id = client_address.client_id
LEFT JOIN
`documents`
ON contracts.contract_id = documents.contract_id
LEFT JOIN
`user_infos`
ON user_infos.user_id = clients.client_rep_id
LEFT JOIN
`users` ON
users.user_id = user_infos.user_id
LEFT JOIN
`location`
ON location.location_id = user_infos.location_id
WHERE client_info.client_role = 'primary'
AND documents.archive = 0
ORDER BY
documents.created_on DESC
Could someone explain to me why my index on client_address isn't being used? I am lost here as I just don't know what to do. It's not that complicated of a query its just rather large. Yes, stackoverflow, I understand the post is mostly code. What more do you want from me? How much more can I say about this other than my query isn't using an index and its causing a really slow query here.
+----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| client_address | CREATE TABLE `client_address` (
`client_address_id` mediumint(12) NOT NULL AUTO_INCREMENT,
`client_id` mediumint(12) NOT NULL,
`client_street` varchar(40) COLLATE utf8_bin NOT NULL,
`client_apartment` varchar(20) COLLATE utf8_bin NOT NULL,
`client_city` varchar(80) COLLATE utf8_bin NOT NULL,
`client_state` varchar(80) COLLATE utf8_bin NOT NULL,
`client_zip` varchar(80) COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`client_address_id`),
KEY `client_id` (`client_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2167 DEFAULT CHARSET=utf8 COLLATE=utf8_bin |
+----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

Optimizing query in the MySQL slow-query log

Our database is set up so that we have a credentials table that hold multiple different types of credentials (logins and the like). There's also a credential_pairs table that associates some of these types together (for instance, a user may have a password and security token).
In an attempt to see if a pair match, there is the following query:
SELECT DISTINCT cp.credential_id FROM credential_pairs AS cp
INNER JOIN credentials AS c1 ON (cp.primary_credential_id = c1.credential_id)
INNER JOIN credentials AS c2 ON (cp.secondary_credential_id = c2.credential_id)
WHERE c1.data = AES_ENCRYPT('Some Value 1', 'encryption key')
AND c2.data = AES_ENCRYPT('Some Value 2', 'encryption key');
This query works fine and gives us exactly what we need. HOWEVER, it is constantly showing in the slow query log (possibly due to lack of indexes?). When I ask MySQL to "explain" the query it gives me:
+----+-------------+-------+------+--------------------------------------------------------+---------------------+---------+-------+-------+--------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+--------------------------------------------------------+---------------------+---------+-------+-------+--------------------------------+
| 1 | SIMPLE | c1 | ref | credential_id_UNIQUE,credential_id,ix_credentials_data | ix_credentials_data | 22 | const | 1 | Using where; Using temporary |
| 1 | SIMPLE | c2 | ref | credential_id_UNIQUE,credential_id,ix_credentials_data | ix_credentials_data | 22 | const | 1 | Using where |
| 1 | SIMPLE | cp | ALL | NULL | NULL | NULL | NULL | 69197 | Using where; Using join buffer |
+----+-------------+-------+------+--------------------------------------------------------+---------------------+---------+-------+-------+--------------------------------+
I have a feeling that last entry (where it shows 69197 rows) is probably the problem, but I am FAR from a DBA... help?
credentials table:
CREATE TABLE `credentials` (
`hidden_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`credential_id` varchar(255) NOT NULL,
`data` blob NOT NULL,
`credential_status` varchar(100) NOT NULL,
`insert_date` datetime NOT NULL,
`insert_user` int(10) unsigned NOT NULL,
`update_date` datetime DEFAULT NULL,
`update_user` int(10) unsigned DEFAULT NULL,
`delete_date` datetime DEFAULT NULL,
`delete_user` int(10) unsigned DEFAULT NULL,
`is_deleted` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`hidden_id`,`credential_id`),
UNIQUE KEY `credential_id_UNIQUE` (`credential_id`),
KEY `credential_id` (`credential_id`),
KEY `data` (`data`(10)),
KEY `credential_status` (`credential_status`(10))
) ENGINE=InnoDB AUTO_INCREMENT=1572 DEFAULT CHARSET=utf8;
credential_pairs Table:
CREATE TABLE `credential_pairs` (
`hidden_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`credential_id` varchar(255) NOT NULL,
`primary_credential_id` varchar(255) NOT NULL,
`secondary_credential_id` varchar(255) NOT NULL,
`is_deleted` tinyint(1) DEFAULT NULL,
PRIMARY KEY (`hidden_id`,`credential_id`),
KEY `primary_credential_id` (`primary_credential_id`(10)),
KEY `secondary_credential_id` (`secondary_credential_id`(10))
) ENGINE=InnoDB AUTO_INCREMENT=500 DEFAULT CHARSET=latin1;
credentials Indexes:
+-------------+------------+----------------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------------+------------+----------------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+
| credentials | 0 | PRIMARY | 1 | hidden_id | A | 186235 | NULL | NULL | | BTREE | |
| credentials | 0 | PRIMARY | 2 | credential_id | A | 186235 | NULL | NULL | | BTREE | |
| credentials | 0 | credential_id_UNIQUE | 1 | credential_id | A | 186235 | NULL | NULL | | BTREE | |
| credentials | 1 | credential_id | 1 | credential_id | A | 186235 | NULL | NULL | | BTREE | |
| credentials | 1 | ix_credentials_data | 1 | data | A | 186235 | 20 | NULL | | BTREE | |
+-------------+------------+----------------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+
credential_pair Indexes:
+------------------+------------+---------------------------------------------+--------------+-------------------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+------------------+------------+---------------------------------------------+--------------+-------------------------+-----------+-------------+----------+--------+------+------------+---------+
| credential_pairs | 0 | PRIMARY | 1 | hidden_id | A | 69224 | NULL | NULL | | BTREE | |
| credential_pairs | 0 | PRIMARY | 2 | credential_id | A | 69224 | NULL | NULL | | BTREE | |
| credential_pairs | 1 | ix_credential_pairs_credential_id | 1 | credential_id | A | 69224 | 36 | NULL | | BTREE | |
| credential_pairs | 1 | ix_credential_pairs_primary_credential_id | 1 | primary_credential_id | A | 69224 | 36 | NULL | | BTREE | |
| credential_pairs | 1 | ix_credential_pairs_secondary_credential_id | 1 | secondary_credential_id | A | 69224 | 36 | NULL | | BTREE | |
+------------------+------------+---------------------------------------------+--------------+-------------------------+-----------+-------------+----------+--------+------+------------+---------+
UPDATE NOTES:
AFAICT: The DISTINCT was superfluous... nothing really needed it, so I dropped it. In an attempt to follow Fabrizio's advice to get a where on the credential_pairs lookup I then altered the statement to read as:
SELECT credential_id
FROM credential_pairs cp
WHERE cp.primary_credential_id = (SELECT credential_id FROM credentials WHERE data = AES_ENCRYPT('value 1','enc_key')) AND
cp.secondary_credential_id = (SELECT credential_id FROM credentials WHERE data = AES_ENCRYPT('value 2','enc_key'))
And.... nothing. The statement takes just as long and the explain looks pretty much the same. So, I added an index to the primary and secondary columns with:
ALTER TABLE credential_pairs ADD INDEX `idx_credential_pairs__primary_and_secondary`(`primary_credential_id`, `secondary_credential_id`);
And... nothing.
+----+-------------+-------------+-------+---------------------+---------------------------------------------+---------+------+-------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------------+-------+---------------------+---------------------------------------------+---------+------+-------+--------------------------+
| 1 | PRIMARY | cp | index | NULL | idx_credential_pairs__primary_and_secondary | 514 | NULL | 69217 | Using where; Using index |
| 3 | SUBQUERY | credentials | ref | ix_credentials_data | ix_credentials_data | 22 | | 1 | Using where |
| 2 | SUBQUERY | credentials | ref | ix_credentials_data | ix_credentials_data | 22 | | 1 | Using where |
+----+-------------+-------------+-------+---------------------+---------------------------------------------+---------+------+-------+--------------------------+
It says it's using the index, but it still looks like it's table scanning. So, I added a joint key (as per a'r's comment below) with:
ALTER TABLE credential_pairs ADD KEY (primary_credential_id, secondary_credential_id);
And... same result as with the index (are these functionally the same?).
The DISTINCT is what is generating the "Use temporary", you usually want to avoid those when possible
Plus you are scanning the whole credential_pair table as you do not have any conditions against it so no indexes are used and the whole table is returned before applying the WHERE
hope it makes sense
EDIT/ADD
Try by starting from a different table, if I understand correctly, you have Table A, a Table B and a Table AB and you are starting the select from AB, try to start it from A
I haven't tested this, but you could try:
SELECT cp.credential_id
FROM credentials AS c1
LEFT JOIN credential_pairs AS cp ON (c1.credential_id = cp.primary_credential_id)
LEFT JOIN credentials AS c2 ON (cp.secondary_credential_id = c2.credential_id)
WHERE
c1.data = AES_ENCRYPT('Some Value 1', 'encryption key')
AND c2.data = AES_ENCRYPT('Some Value 2', 'encryption key');
I have had luck in the past by moving select tables around