繁体   English   中英

为where子句和order_by创建MYSQL索引

[英]Create a MYSQL index for where clause and order_by

考虑到这张表,

CREATE TABLE tbl_tax (
  taxdata_id int(11) NOT NULL AUTO_INCREMENT,
  tax_year varchar(255) NOT NULL,
  display_pid varchar(255) NOT NULL,
  type varchar(255) NOT NULL,
  tax_id varchar(255) NOT NULL,
  tax_amount varchar(255) NOT NULL,
  total_due varchar(255) NOT NULL,
  paid_wcert varchar(255) NOT NULL,
  datelast_adv varchar(255) NOT NULL,
  pmtmade_today varchar(255) NOT NULL,
  owner_name varchar(255) NOT NULL,
  PRIMARY KEY (taxdata_id),
  UNIQUE KEY unique_tbl_tax_TaxidYear (tax_id,tax_year),
  KEY tax_year_2 (tax_year, owner_name, tax_id, display_pid, 
    type, tax_amount, total_due, total_paid, datelast_adv, pmtmade_today, 
    taxdata_id, paid_wcert)
) ENGINE=InnoDB AUTO_INCREMENT=100000 DEFAULT CHARSET=latin1;
 tbl_tax;

考虑到这个 SQL 查询,

SELECT tax_year
     , tax_id
     , owner_name
     , display_pid
     , type
     , tax_amount
     , total_due
     , total_paid
     , datelast_adv
     , pmtmade_today
     , taxdata_id
     , paid_wcert
  FROM tbl_tax
 WHERE tax_year >= '2015'
   AND tax_year <= '2019'
 ORDER 
    BY tax_year DESC;

我想创建一个索引并尝试创建一个封面索引。

引用这篇文章,“一般规则是首先选择要过滤的列(具有相等条件的 WHERE 子句),然后是排序/分组(ORDER BY 和 GROUP BY 子句),最后是数据投影(SELECT 子句)。”

ALTER TABLE tbl_tax
ADD INDEX (
    `tax_year`, `owner_name`, `tax_id`, `display_pid`, 
    `type`, `tax_amount`, `total_due`, `total_paid`, `datelast_adv`, `pmtmade_today`, 
    `taxdata_id`, `paid_wcert`
);

做一个explain ,显示,

        "id" : 1,
        "select_type" : "SIMPLE",
        "table" : "tbl_tax",
        "partitions" : null,
        "type" : "index",
        "possible_keys" : "tax_year_2",
        "key" : "tax_year_2",
        "key_len" : "2831",
        "ref" : null,
        "rows" : 271630,
        "filtered" : 50.00,
        "Extra" : "Using where; Backward index scan; Using index"   

在创建索引时,我知道:-

  1. WHERE 子句包括范围谓词 (<=, >=)
  2. 查询包含 ORDER_BY 的顺序与访问行的顺序不同。

这些可能是explain的 output 显示"rows": 271630,

但是,SQL 查询的结果集只有 ~2000 行。

尝试阅读许多文章,但我仍在努力优化它。

我该怎么做才能获得更好的优化? 我可以用更好的方式创建索引吗? 我可以对 SQL 查询进行任何更改吗? 另外,如果我在这里误解了什么,请随时纠正我。

这是一个有趣的案例,因为通常我们希望在 EXPLAIN 计划中看到Using index ,但在这种情况下这是一种损害。

原因是这是type: index ,这意味着它正在进行索引扫描。 这意味着它正在扫描整个索引,而不仅仅是符合您条件的行。 这就是它显示rows: 271630的原因。 这基本上就是您的表的大小(或者至少是优化器根据其统计信息估计的表的大小)。

在这种情况下,我认为将每一列添加到索引中没有帮助。 使用列的索引会更好: tax_year

然后我希望 EXPLAIN 显示type: range因为你的条件,这表明它正在检查的唯一行是那些匹配条件的行。

然后我们会看到Filtered: 100.00这表明所有检查的行都包含在结果中,这很好。 这意味着查询是有效的,因为没有行被检查但随后被过滤掉。

此外,由于您的 ORDER BY 是针对同一列的,因此我仍然希望Using filesort不存在,这很好。


回复您的评论:

我想您在 2015 年至 2019 年之间的 tax_year 条件与表的很大一部分相匹配。 如果您的条件匹配大部分行,MySQL 选择不使用索引。 它估计使用索引比只扫描表的成本更高。

如果你认为优化器做出了错误的选择,你可以给它一个提示,应该假定表扫描的成本更高:

... FROM tbl_tax FORCE INDEX(tax_year) ...

(我假设索引的名称是tax_year ,但你应该用你的情况下的索引名称替换它。)

我也同意其他人的观点,即您对每个属性列使用varchar(255)是不合适的。

INDEX(tax_year, ...)确实处理WHEREORDER BY

查询包含 ORDER_BY 的顺序与访问行的顺序不同。

错误的。 WHERE没有指定访问它们的顺序。 实际上EXPLAIN说的是“向后索引扫描”。 一切都很好。

使用合理的数据类型,例如tax_year的 2 字节YEAR而不是VARCHAR(255) ,它需要 6 个字节来表示一年。

varchars 的算术(“数量”等)会很混乱。

当然,“覆盖”索引有一点帮助。 但我不喜欢让索引大于 5 列。 你的大索引有助于查询一些,但也会伤害一些INSERTs

(我同意比尔的观点。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM