mysql use the index optimization sorting! - mysql

First, I'm creating table tag:
CREATE TABLE `tag` (
`id` smallint(6) NOT NULL AUTO_INCREMENT,
`total` int(11) DEFAULT NULL,
`total_question` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_sort` (`total`,`total_question`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=gb2312;
mysql> explain select * from tag order by total;
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
| 1 | SIMPLE | tag | index | NULL | idx_sort | 10 | NULL | 1 | Using index |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
Sort using index, not using filesort.
When I add column name to tag table:
CREATE TABLE `tag` (
`id` smallint(6) NOT NULL AUTO_INCREMENT,
`total` int(11) DEFAULT NULL,
`total_question` int(11) DEFAULT NULL,
`name` char(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_sort` (`total`,`total_question`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=gb2312;
mysql> explain select * from tag order by total;
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
| 1 | SIMPLE | tag | ALL | NULL | NULL | NULL | NULL | 1 | Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
Sort using filesort, not using index.
When I create index only on total:
CREATE TABLE `tag` (
`id` smallint(6) NOT NULL AUTO_INCREMENT,
`total` int(11) DEFAULT NULL,
`total_question` int(11) DEFAULT NULL,
`name` char(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_sort` (`total`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=gb2312;
mysql> explain select * from tag order by total;
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
| 1 | SIMPLE | tag | ALL | NULL | NULL | NULL | NULL | 1 | Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
Sort using filesort! Why? I only use the total column for sort.

you can run desc select * from tag force index (idx_sort) order by total ;
you can see the output :
mysql> desc select * from tag force index (idx_sort) order by total ;
+----+-------------+-------+-------+---------------+----------+---------+------+------+--- ----+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+----------+---------+------+------+--- ----+
| 1 | SIMPLE | tag | index | NULL | idx_sort | 5 | NULL | 1 | |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------+

Related

Mysql, slow avg query

I have a single table book_log Mysql 5.7
+------------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+----------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| book_id | int(11) | YES | MUL | NULL | |
| type | int(11) | NO | MUL | NULL | |
| value | int(11) | YES | | NULL | |
| created_at | datetime | NO | | NULL | |
+------------+----------+------+-----+---------+----------------+
Book table makes connection with series (One series can have many books)
Create table info :
book_log | CREATE TABLE `book_log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`book_id` int(11) DEFAULT NULL,
`type` int(11) NOT NULL,
`value` int(11) DEFAULT NULL,
`created_at` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `IDX_7E42115316A2B381` (`book_id`),
KEY `IDX_TYPE` (`type`),
KEY `IDX_ME` (`book_id`,`type`) USING BTREE,
CONSTRAINT `FK_7E42115316A2B381` FOREIGN KEY (`book_id`) REFERENCES `book` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1158962 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci |
book | CREATE TABLE `book` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`series_id` int(11) DEFAULT NULL,
`language_id` int(11) DEFAULT NULL,
`position` int(11) NOT NULL,
`dir` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `IDX_CBE5A3315278319C` (`series_id`),
KEY `IDX_CBE5A33182F1BAF4` (`language_id`),
CONSTRAINT `FK_CBE5A3315278319C` FOREIGN KEY (`series_id`) REFERENCES `series` (`id`),
CONSTRAINT `FK_CBE5A33182F1BAF4` FOREIGN KEY (`language_id`) REFERENCES `language` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=55022 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci |
I make the avg value for a given series
select AVG(value)
from book_log
join book b on book_log.book_id = b.id
where type = 20 and b.series_id = ?;
Explain :
+----+-------------+----------+------------+------+------------------------------+----------------------+---------+----------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+------+------------------------------+----------------------+---------+----------------+------+----------+-------------+
| 1 | SIMPLE | b | NULL | ref | PRIMARY,IDX_CBE5A3315278319C | IDX_CBE5A3315278319C | 5 | const | 212 | 100.00 | Using index |
| 1 | SIMPLE | book_log | NULL | ref | IDX_7E42115316A2B381,IDX_ME | IDX_7E42115316A2B381 | 5 | bdd.b.id | 33 | 100.00 | NULL |
+----+-------------+----------+------------+------+------------------------------+----------------------+---------+----------------+------+----------+-------------+
Or
select AVG(value)
from book_log
where type = 20 AND book_id IN (
select id from book where series_id = ?
);
Explain :
+----+-------------+----------+------------+------+--------------------------------------+----------------------+---------+-------------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+------+--------------------------------------+----------------------+---------+-------------------+------+----------+-------------+
| 1 | SIMPLE | book | NULL | ref | PRIMARY,IDX_CBE5A3315278319C | IDX_CBE5A3315278319C | 5 | const | 212 | 100.00 | Using index |
| 1 | SIMPLE | book_log | NULL | ref | IDX_7E42115316A2B381,IDX_TYPE,IDX_ME | IDX_7E42115316A2B381 | 5 | bdd.book.id | 33 | 3.72 | Using where |
+----+-------------+----------+------------+------+--------------------------------------+----------------------+---------+-------------------+------+----------+-------------+
I have 10 973 results for these query, 42ms for a count(*) but more than 1 sec for the avg query.
I don't understand why is it so long.
Any idea ?
Thx.
You can expect to COUNT(*) be as fast or faster than SUM(somecol) or AVG(othercolumn). Why? The database server is, by the rules of SQL, to apply any optimization that yields the correct anwer. COUNT(*) has some serious optimization to it.
But the aggregate functions that do arithmetic ; they must instead examine every record. So, slower.
You can create a purpose-built index for your query.
It is this:
ALTER TABLE book_log
ADD INDEX type_id_val
(type, book_id, val)
I chose these columns for the index because your query searches for a particuler type in the index. Upon finding the first row of the chosen type, MySQL can range-scan through just the index and not the table. So, faster. It's called a covering index.

Update query not making full use of index in MySQL 8

Given this table:
CREATE TABLE `queue` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`type` int(10) unsigned NOT NULL,
`posted_on` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
`status` enum('pending','complete','error') NOT NULL DEFAULT 'pending',
`body` blob NOT NULL,
`process_id` int(10) unsigned DEFAULT NULL,
`acquired_on` datetime(6) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `acquiredon` (`acquired_on`),
KEY `type_status_processid_postedon` (`type`,`status`,`process_id`,`posted_on`) USING BTREE
);
When I do a select on this table, it makes proper/full use of the index:
EXPLAIN SELECT *
FROM `queue`
FORCE INDEX (`type_status_processid_postedon`)
WHERE type = 1
AND `status` = 'pending'
AND `process_id` IS NULL
ORDER BY `posted_on` ASC
LIMIT 1;
+----+-------------+-------+------------+------+--------------------------------+--------------------------------+---------+-------------------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+--------------------------------+--------------------------------+---------+-------------------+------+----------+-----------------------+
| 1 | SIMPLE | queue | NULL | ref | type_status_processid_postedon | type_status_processid_postedon | 10 | const,const,const | 1 | 100.00 | Using index condition |
+----+-------------+-------+------------+------+--------------------------------+--------------------------------+---------+-------------------+------+----------+-----------------------+
And yet, when I do the same query as an UPDATE, the index is not fully used.
EXPLAIN UPDATE `queue`
FORCE INDEX(`type_status_processid_postedon`)
SET `process_id` = 1
WHERE `type` = 1
AND `status` = 'pending'
AND `process_id` IS NULL
ORDER BY `posted_on` ASC
LIMIT 1;
+----+-------------+-------+------------+-------+--------------------------------+--------------------------------+---------+-------------------+------+----------+-----------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+--------------------------------+--------------------------------+---------+-------------------+------+----------+-----------------------------+
| 1 | UPDATE | queue | NULL | range | type_status_processid_postedon | type_status_processid_postedon | 10 | const,const,const | 1 | 100.00 | Using where; Using filesort |
+----+-------------+-------+------------+-------+--------------------------------+--------------------------------+---------+-------------------+------+----------+-----------------------------+
The update does a filesort. What's going on here?

Use of MySQL explain

I have this query:
EXPLAIN EXTENDED
SELECT DISTINCT
PMS_STAGIONI.DINIZVAL,
PMS_STAGIONI.DFINEVAL,
PMS_DISPO.DDATA
FROM
PMS_DISPO JOIN PMS_STAGIONI
HAVING
PMS_DISPO.DDATA BETWEEN PMS_STAGIONI.DINIZVAL AND PMS_STAGIONI.DFINEVAL
The output of explain is:
+----+-------------+--------------+-------+---------------+------------------------------+---------+------+------+----------+--------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------+-------+---------------+------------------------------+---------+------+------+----------+--------------------------------+
| 1 | SIMPLE | PMS_STAGIONI | index | NULL | IDX_INIZFINEVAL_PMS_STAGIONI | 6 | NULL | 3 | 100.00 | Using index; Using temporary |
| 1 | SIMPLE | PMS_DISPO | index | NULL | IDX_DDATA_PMS_DISPO | 3 | NULL | 1199 | 100.00 | Using index; Using join buffer |
+----+-------------+--------------+-------+---------------+------------------------------+---------+------+------+----------+--------------------------------+
My question is how to calculate the product of the join using explain. For example, in this case are performed 3597 (1199x3) scans or only 1199?
1)If I add "ORDER BY DDATA" lines scanned in the table "PMS_DISPO" become 1130.
2)If I use the "WHERE" clause instead of "HAVING" clause scan no longer uses the indexes. How is it possible?
3)If i want show PMS_STAGIONI.CSTAGIONI (primary key) explain show me that:
+----+-------------+--------------+-------+---------------+---------------------+---------+------+------+----------+--------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------+-------+---------------+---------------------+---------+------+------+----------+--------------------------------+
| 1 | SIMPLE | PMS_STAGIONI | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | Using temporary |
| 1 | SIMPLE | PMS_DISPO | index | NULL | IDX_DDATA_PMS_DISPO | 3 | NULL | 1130 | 100.00 | Using index; Using join buffer |
+----+-------------+--------------+-------+---------------+---------------------+---------+------+------+----------+--------------------------------+
How can I force the use of the other index?
Thanks in advance.
Edit:
The structure of "PMS_DISPO" is:
CREATE TABLE IF NOT EXISTS `PMS_DISPO` (
`ID` int(11) NOT NULL AUTO_INCREMENT ,
`CPRENOTA` int(11) NOT NULL,
`DDATA` date NOT NULL,
`CCATRIS` int(4) NOT NULL,
`NQUANT` int(4) NOT NULL,
`CAZIENDA` int(4) NOT NULL,
`CAFFILIATO` int(4) NOT NULL,
PRIMARY KEY (`ID`),
KEY `IDX_DDATA_PMS_DISPO` (`DDATA`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1084 ;
And "PMS_STAGIONI" is:
CREATE TABLE IF NOT EXISTS `PMS_STAGIONI` (
`CSTAGIONE` int(11) NOT NULL,
`NVALIDI` tinyint(2) NOT NULL,
`BECCEZIONE` tinyint(1) NOT NULL,
`AGGSET` varchar(7) DEFAULT NULL,
`DINIZVAL` date NOT NULL,
`DFINEVAL` date NOT NULL,
`CAZIENDA` int(4) NOT NULL,
`ID` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`ID`),
KEY `CSTAGIONE` (`CSTAGIONE`),
KEY `IDX_INIZFINEVAL_PMS_STAGIONI` (`DINIZVAL`,`DFINEVAL`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;
A query of this sort would normally be written as follows, with indexes just as you have them...
SELECT DISTINCT s.dinizval
, s.dfineval
, d.ddata
FROM pms_dispo d
JOIN pms_stagioni s
ON d.ddata BETWEEN s.dinizval AND s.dfineval

accelerate MySQL query

This is the shortversion of my query:
SELECT product.* FROM product_list product
LEFT JOIN language_item language ON (product.title=language.languageVariable)
WHERE language.languageID = 1
ORDER BY language.languageValue ASC
When I use it, the query has 3 seconds. When I remove the order by the query has 0.3 seconds. Can you recommend a change to accelerate it?
product.title and language.languageVariable is a language variable like global.product.title1, and languageValue is the title like car, doll or something else.
CREATE TABLE `language_item` (
`languageItemID` int(10) UNSIGNED NOT NULL,
`languageID` int(10) UNSIGNED NOT NULL DEFAULT '0',
`languageVariable` varchar(255) NOT NULL DEFAULT '',
`languageValue` mediumtext NOT NULL,
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
ALTER TABLE `language_item`
ADD PRIMARY KEY (`languageItemID`),
ADD UNIQUE KEY `languageVariable` (`languageVariable`,`languageID`),
ADD KEY `languageValue` (`languageValue`(300));
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra
1 | SIMPLE | product | NULL | ALL | PRIMARY,inactive,archive,productCategoryID | NULL | NULL | NULL | 1475 | 88.27 | Using where; Using temporary; Using filesort
1 | SIMPLE | language | NULL | ref | languageVariable | languageVariable | 767 | db.product.title | 136 | 1.00 | Using index condition
Here is the structur from language_item with the index:
CREATE TABLE `language_item` (
`languageItemID` int(10) UNSIGNED NOT NULL,
`languageID` int(10) UNSIGNED NOT NULL DEFAULT '0',
`languageVariable` varchar(255) NOT NULL DEFAULT '',
`languageValue` mediumtext NOT NULL,
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
ALTER TABLE `language_item`
ADD PRIMARY KEY (`languageItemID`),
ADD UNIQUE KEY `languageVariable` (`languageVariable`,`languageID`),
ADD KEY `languageValue` (`languageValue`(300));
The Explain:
id | select_type | table | partitions | type | possible_keys | key |
key_len | ref | rows | filtered | Extra 1 | SIMPLE | product | NULL |
ALL | PRIMARY,inactive,archive,productCategoryID | NULL | NULL | NULL
| 1475 | 88.27 | Using where; Using temporary; Using filesort 1 |
SIMPLE | language | NULL | ref | languageVariable | languageVariable |
767 | db.product.title | 136 | 1.00 | Using index condition
TRy this:
SELECT d.* from (
SELECT product.*, language.languageValue AS lv
FROM product_list product
JOIN language_item language ON (product.title=language.languageVariable)
WHERE language.languageID = 1
) as d
ORDER BY d.lv ASC

MySQL index isn't used in simple select query

I have a table
CREATE TABLE `pd` (
`product_id` int(11) NOT NULL AUTO_INCREMENT,
`language_id` int(11) NOT NULL,
`name` varchar(255) NOT NULL,
`description` text NOT NULL,
`meta_description` varchar(255) NOT NULL,
`meta_keyword` varchar(255) NOT NULL,
`seo_title` varchar(255) NOT NULL,
`seo_h1` varchar(255) NOT NULL,
PRIMARY KEY (`product_id`,`language_id`),
KEY `language_id` (`description`(128),`language_id`),
KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=47019 DEFAULT CHARSET=utf8;
When I run this query
EXPLAIN SELECT * FROM `pd` ORDER BY product_id;
I get this result:
+----+-------------+-------+-------+---------------+---------+---------+------+--------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+------+--------+-------+
| 1 | SIMPLE | pd | index | NULL | PRIMARY | 8 | NULL | 139551 | |
+----+-------------+-------+-------+---------------+---------+---------+------+--------+-------+
When I run this query
EXPLAIN SELECT * FROM `pd` ORDER BY name;
I get this result:
+----+-------------+-------+------+---------------+------+---------+------+--------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+--------+----------------+
| 1 | SIMPLE | pd | ALL | NULL | NULL | NULL | NULL | 137762 | Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+--------+----------------+
Why in second case index isn't used? Only difference I see is product_id is part of primary key and name is non-unique index.
As soon as the name isn't a clustered key - it will be much more expensive to read the index first and seek for every row one by one in data.
So mysql (and I think that any other would DBMS) decdies to read the data and sort it in-memory.