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.
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
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!
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 |
+----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
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