【问题标题】:How to optimize a query using a range with Mysql如何使用 Mysql 使用范围优化查询
【发布时间】:2015-11-28 08:46:12
【问题描述】:

我尝试优化这个查询:

select id_store from receipt where receiptDate between '20151109' and '20151116'

我使用命令EXPLAIN 执行此查询。似乎没有使用任何密钥。不使用receiptDate 的索引。怎么了 ?

这是表receipt的结构:

CREATE TABLE receipt (
id_store tinyint(3) unsigned NOT NULL default '0',
id_receipt int(7) unsigned NOT NULL default '0',
id_product smallint(6) unsigned NOT NULL default '0',
receiptDate char(8) NOT NULL default '',
qty float NOT NULL default '0',
turnover float NOT NULL default '0',
PRIMARY KEY  (id_store,id_receipt,id_product,receiptDate),
KEY NDX_1 (receiptDate),
) ENGINE=MEMORY;

这是命令EXPLAIN 的结果:

+----+-------------+---------------------+--------+-----------------------------------------------------------+---------------------------------+---------+--------------------------------------+------+---------------------------------+
| id | select_type | table               | type   | possible_keys                                             | key                             | key_len | ref                                  | rows | Extra                           |
+----+-------------+---------------------+--------+-----------------------------------------------------------+---------------------------------+---------+--------------------------------------+------+---------------------------------+
| 1  | SIMPLE      | receipt             |   ALL  |NDX_1                                                      |                                 |         |                                      |24789225|  Using where | 
+----+-------------+---------------------+--------+-----------------------------------------------------------+---------------------------------+---------+--------------------------------------+------+---------------------------------+

receipt 表包含 24.789.225 行,平均每天有 15.000 行(receiptDate)。 我执行以下查询并获得 120.295 行:

select count(*) from receipt where receiptDate between '20151109' and '20151116'

提前感谢您的帮助。

【问题讨论】:

  • 您应该发布表结构并解释结果。我们看不到您的屏幕
  • 您还可以发布数据分布 select COUNT(),receiptDate from receipt group by receiptDate 并运行相同的查询,但使用您的 where 条件。使用两个结果编辑您的问题,我怀疑基数不佳。
  • 您的查询返回表中的所有行,因此使用索引没有意义。当您需要一小部分数据时,索引很有用。
  • 在INT中转换receiptDate
  • 米海,谢谢,但是如果您阅读我的帖子,查询返回 120.295 行。该表包含 24.789.225 行。

标签: mysql optimization range


【解决方案1】:

由于您索引了receiptDate,如果优化器认为数据库引擎将使用此索引会提高性能。优化器根据有关您的表的统计信息做出决定。它在后台创建这些统计信息,这是一个非常透明的过程。

现在您正在使用MEMORY 引擎。因为内存表应该是短暂的,所以这些特殊引擎的优化器功能非常有限。您可能希望强制查询使用带有 FORCE INDEX 关键字的索引。

您的日期存储为CHAR(8),这很慢,因为引擎必须解析所有高度CHAR。您将使用INT(将日期转换为YYYYMMDD)获得改进的性能。您的查询应该仍然有效,因为引擎会自动将字符串输入转换为 int。

如果你打算使用InnoDB引擎,那么你应该尽可能把日期作为主键,因为这个引擎的主键也是Clustered Index,这意味着数据将按物理排序存储日期。

【讨论】:

    【解决方案2】:

    receiptDate 使用 3 字节 DATE 数据类型,而不是 8 字节(或 24 字节,如果 utf8)CHAR(8)。

    当您使用它时,您可以通过将id_receipt MEDIUMINT UNSIGNED 设置为只有 7 位数字来节省另一个字节。

    指定 BTREE:

    KEY `NDX_1` (`receiptDate`) USING BTREE
    

    因为MEMORY 可能会将其设为HASH 索引。 BTREE 可以处理范围; HASH 必须进行表扫描。

    我同意 InnoDB 总体上可能会更好。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-28
      • 2021-06-01
      • 1970-01-01
      • 2014-07-31
      • 2018-11-01
      相关资源
      最近更新 更多