Here is my table :
In my table
Clustering_key
(Primary key and auto incremental)ID
(Index Column)Data
(Text datatype column)Position
(Index column) maintain the order of Data
My table have 90,000 rows with same ID
equal to 5. I want to first 3 rows with ID
equal to 5 and my query like this
Select * from mytable where ID=5 Limit 3;
ID
column is index column So I think mysql scan only first 3 rows but mysql scan around 42000 rows.
Here Explain query :
Any possibility to avoid all rows scan.
Please give me some solution
Thanks in advance
Limit the Rows Returned in a SQL Server Query by using the TOP Clause. In SQL Server, you can use the TOP clause to limit the rows returned from a query result set. This clause provides similar functionality to LIMIT in MySQL, and ROWNUM in Oracle, although there are differences in how each of these work.
When a LIMIT clause is appended to a SELECT statement, it does not affect whether or not a full table scan is performed. However, the LIMIT clause affects the number of results returned for the query, but not how many rows are scanned for a table.
The SQL LIMIT clause constrains the number of rows returned by a SELECT statement. For Microsoft databases like SQL Server or MSAccess, you can use the SELECT TOP statement to limit your results, which is Microsoft's proprietary equivalent to the SELECT LIMIT statement.
EDIT: For SQL Server, replace LIMIT @num with TOP @num before the UNION ALL in each query and replace the DEFAULT with = . You can also have a second line to declare the @num as a string and use the PERCENT keyword, but only in SQL Server as neither MySQL nor Oracle supports it.
I simulated the scenario.
CREATE TABLE mytable ( Clustering_key INT NOT NULL AUTO_INCREMENT, ID INT NOT NULL, Data text NOT NULL, Position INT NOT NULL, PRIMARY KEY (Clustering_key), KEY(ID), KEY(Position) )
INSERT INTO mytable (ID,Data,Position) VALUES (5,CONCAT("Data-",5), 7); INSERT INTO mytable (ID,Data,Position) VALUES (5,CONCAT("Data-",5), 26); INSERT INTO mytable (ID,Data,Position) VALUES (5,CONCAT("Data-",51), 27); INSERT INTO mytable (ID,Data,Position) VALUES (5,CONCAT("Data-",56), 28); INSERT INTO mytable (ID,Data,Position) VALUES (5,CONCAT("Data-",57), 31);
mysql> explain Select * from mytable where ID=5 Limit 3 +----+-------------+---------+------------+------+---------------+------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------+------+---------+-------+------+----------+-------+ | 1 | SIMPLE | mytable | NULL | ref | ID | ID | 4 | const | 5 | 100.00 | NULL | +----+-------------+---------+------------+------+---------------+------+---------+-------+------+----------+-------+ 1 row in set, 1 warning (0.00 sec)
Yes, the explain
shows rows examined is 5, but not 3.
But seems it is just a misleading info.
The exact number of run-time rows_examined can be verified by enabling slow log for all queries(Setting long_query_time=0) by following steps.
Note: You MUST set long_query_time=0 only in your own testing database. And you MUST reset the parameter back to the previous value after the testing.
- set GLOBAL slow_query_log=1; - set global long_query_time=0; - set session long_query_time=0; mysql> show variables like '%slow%'; +---------------------------+-------------------------------------------------+ | Variable_name | Value | +---------------------------+-------------------------------------------------+ | log_slow_admin_statements | OFF | | log_slow_slave_statements | OFF | | slow_launch_time | 2 | | slow_query_log | ON | | slow_query_log_file | /usr/local/mysql/data/slow.log | +---------------------------+-------------------------------------------------+ 5 rows in set (0.10 sec) mysql> select @@long_query_time; +-------------------+ | @@long_query_time | +-------------------+ | 0.000000 | +-------------------+
And then in the terminal, executing the query
<pre>
mysql> Select * from mytable where ID=5 Limit 3;
+----------------+----+---------+----------+
| Clustering_key | ID | Data | Position |
+----------------+----+---------+----------+
| 5 | 5 | Data-5 | 7 |
| 26293 | 5 | Data-5 | 26 |
| 26294 | 5 | Data-51 | 27 |
+----------------+----+---------+----------+
3 rows in set (0.00 sec)
mysql> Select * from mytable where ID=5 Limit 1;
Checking the slow log by inspecting the slow_query_log_file
printed above /usr/local/mysql/data/slow.log
You can find out the info as below.
# Time: 2019-04-26T01:48:19.890846Z # User@Host: root[root] @ localhost [] Id: 5124 # Query_time: 0.000575 Lock_time: 0.000146 Rows_sent: 3 Rows_examined: 3 SET timestamp=1556243299; Select * from mytable where ID=5 Limit 3; # Time: 2019-04-26T01:48:34.672888Z # User@Host: root[root] @ localhost [] Id: 5124 # Query_time: 0.000182 Lock_time: 0.000074 Rows_sent: 1 Rows_examined: 1 SET timestamp=1556243314; Select * from mytable where ID=5 Limit 1;
The runtime Rows_exmained
value is equal to the value of limit
parameter.
The test is done on MySQL 5.7.18.
----------------------------------Another way to verify----------------------------------
mysql> show status like '%Innodb_rows_read%'; +------------------+-------+ | Variable_name | Value | +------------------+-------+ | Innodb_rows_read | 13 | +------------------+-------+ 1 row in set (0.00 sec) mysql> Select * from mytable where ID=5 Limit 1; +----------------+----+--------+----------+ | Clustering_key | ID | Data | Position | +----------------+----+--------+----------+ | 5 | 5 | Data-5 | 7 | +----------------+----+--------+----------+ 1 row in set (0.00 sec) mysql> show status like '%Innodb_rows_read%'; +------------------+-------+ | Variable_name | Value | +------------------+-------+ | Innodb_rows_read | 14 | +------------------+-------+ 1 row in set (0.00 sec)
You can see the Innodb_rows_read
just be increased 1 for limit 1.
If you do a full table scan query, you can see the value will be increased by the count of the table.
mysql> select count(*) from mytable; +----------+ | count(*) | +----------+ | 126296 | +----------+ 1 row in set (0.05 sec) mysql> show status like '%Innodb_rows_read%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | Innodb_rows_read | 505204 | +------------------+--------+ 1 row in set (0.00 sec) mysql> Select * from mytable where Data="Data-5"; +----------------+----+--------+----------+ | Clustering_key | ID | Data | Position | +----------------+----+--------+----------+ | 5 | 5 | Data-5 | 7 | | 26293 | 5 | Data-5 | 26 | | 26301 | 5 | Data-5 | 7 | +----------------+----+--------+----------+ 3 rows in set (0.09 sec) mysql> show status like '%Innodb_rows_read%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | Innodb_rows_read | 631500 | +------------------+--------+ 1 row in set (0.00 sec)
Both ways confirmed the explain
for limit seems providing misleading info about rows examined.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With