mysql - 如何让 MySQL 使用 INDEX 进行 View 查询?

标签 mysql indexing

我正在使用 Java EE 上的 MySql 数据库开发 Web 项目。我们需要一个 View 来汇总 3 个总行超过 300 万行的表中的数据。每个表都是用索引创建的。但是我还没有找到一种方法可以从我们使用 [group by] 创建的 View 中利用条件选择语句检索中的索引。

我收到了来自 的人的建议在 MySql 中使用 View 不是一个好主意 .因为您不能像在 oracle 中那样为 mysql 中的 View 创建索引。但是在我进行的一些测试中,可以在 View 选择语句中使用索引。也许我以错误的方式创建了这些 View 。

我将用一个例子来描述我的问题。

我们有一个记录 NBA 比赛高分数据的表格,在列 [happend_in] 上有索引

CREATE  TABLE `highscores` (
   `tbl_id` int(11) NOT NULL auto_increment,
   `happened_in` int(4) default NULL,
   `player` int(3) default NULL,
   `score` int(3) default NULL,
   PRIMARY KEY  (`tbl_id`),
   KEY `index_happened_in` (`happened_in`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

插入数据(8 行)
INSERT INTO highscores(happened_in, player, score)
VALUES (2006, 24, 61),(2006, 24, 44),(2006, 24, 81),
(1998, 23, 51),(1997, 23, 46),(2006, 3, 55),(2007, 24, 34), (2008, 24, 37);

然后我创建一个 View 来查看科比·布莱恩特每年获得的最高分
CREATE OR REPLACE VIEW v_kobe_highScores
AS
   SELECT player, max(score) AS highest_score, happened_in
   FROM highscores
   WHERE player = 24
   GROUP BY happened_in;

我写了个条件语句看最高分是神户进入 2006 ;
select * from v_kobe_highscores where happened_in = 2006;

我在toad for mysql里解释的时候发现mysql有扫描所有行形成 View ,然后在其中查找具有条件的数据,而不使用 [happened_in] 上的索引。
explain select * from v_kobe_highscores where happened_in = 2006;

explain result

我们在项目中使用的 View 是在具有数百万行的表中构建的。在每个 View 数据检索中扫描表中的所有行是 Not Acceptable 。请帮忙!谢谢!

@zerkms 这是我在现实生活中测试的结果。我看不出有多大区别。我认为@spencer7593 的观点是正确的。 MySQL 优化器不会在 View 查询中“推送”该谓词。
real-life test

最佳答案

如何让 MySQL 使用索引进行 View 查询?简短的回答,提供一个 MySQL 可以使用的索引。

在这种情况下,最佳索引可能是“覆盖”索引:

... ON highscores (player, happened_in, score)

MySQL 很可能会使用该索引,并且 EXPLAIN 将显示:"Using index"由于 WHERE player = 24 (索引中前导列的相等谓词。GROUP BY happened_id(索引中的第二列)可能允许 MySQL 优化使用索引以避免排序操作。在索引中包含 score 列将允许查询完全满足索引,而不必访问(查找)索引引用的数据页。

这就是快速答案。更长的答案是 MySQL 不太可能使用前导列 happened_id 的索引。用于 View 查询。

为什么 View 会导致性能问题

MySQL View 的问题之一是 MySQL 不会将谓词从外部查询“推送”到 View 查询中。

您的外部查询指定 WHERE happened_in = 2006 . MySQL 优化器在运行内部“ View 查询”时不考虑谓词。该 View 查询在外部查询之前单独执行。执行该查询的结果集被“物化”;也就是说,结果存储为一个中间 MyISAM 表。 (MySQL 称其为“派生表”,当您了解 MySQL 执行的操作时,他们使用的名称是有道理的。)

底线是您在 happened_in 上定义的索引当 MySQL 运行形成 View 定义的查询时,它没有被 MySQL 使用。

创建中间“派生表”后,然后执行外部查询,使用该“派生表”作为行源。当外部查询运行时 happened_in = 2006谓词被评估。

请注意, View 查询中的所有行都已存储,(在您的情况下)是 happened_in 的每个值的行,而不仅仅是您在外部查询中指定相等谓词的那个。

处理 View 查询的方式可能有些人“出乎意料”,这就是与其他关系数据库处理 View 查询的方式相比,在 MySQL 中使用“ View ”会导致性能问题的原因之一。

使用合适的覆盖索引提高 View 查询的性能

给定您的 View 定义和查询,您将获得的最好的方法是 View 查询的“使用索引”访问方法。为此,您需要一个覆盖索引,例如
... ON highscores (player, happened_in, score).

对于现有 View 定义和现有查询,这可能是最有益的索引(性能方面)。 player column 是前导列,因为您在 View 查询中对该列有一个相等谓词。 happened_in列是下一个,因为您对该列进行了 GROUP BY 操作,并且 MySQL 将能够使用此索引来优化 GROUP BY 操作。我们还包括 score列,因为这是您的查询中唯一引用的其他列。这使得索引成为“覆盖”索引,因为 MySQL 可以直接从索引页面满足该查询,而无需访问基础表中的任何页面。这与我们将要退出查询计划一样好:“​​使用索引”而不是“使用文件排序”。

将性能与没有派生表的独立查询进行比较

您可以将查询的执行计划与 View 与等效的独立查询进行比较:
SELECT player
     , MAX(score) AS highest_score
     , happened_in
 FROM highscores
WHERE player = 24
  AND happened_in = 2006
GROUP
   BY player
    , happened_in

独立查询也可以使用覆盖索引,例如
... ON highscores (player, happened_in, score)

但无需实现中间 MyISAM 表。

我不确定以前的任何一个都为您提出的问题提供了直接答案。

问:如何让 MySQL 使用 INDEX 进行 View 查询?

A:定义 View 查询可以使用的合适的INDEX。

简短的回答是提供一个“覆盖索引”(索引包括 View 查询中引用的所有列)。该索引中的前导列应该是用等式谓词引用的列(在您的情况下,列 player 将是前导列,因为您在查询中有一个 player = 24 谓词。此外,在GROUP BY 应该是索引中的前导列,这允许 MySQL 通过使用索引而不是使用排序操作来优化 GROUP BY 操作。

这里的关键是 View 查询基本上是一个独立的查询;该查询的结果存储在一个中间的“派生”表(一个 MyISAM 表,该表在针对 View 的查询运行时创建。

在 MySQL 中使用 View 不一定是一个“坏主意”,但我强烈提醒那些选择在 MySQL 中使用 View 的人要注意 MySQL 如何处理引用这些 View 的查询。并且 MySQL 处理 View 查询的方式(显着地)不同于其他数据库(例如 Oracle、SQL Server)处理 View 查询的方式。

关于mysql - 如何让 MySQL 使用 INDEX 进行 View 查询?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13944946/

相关文章:

mysql - 什么是插入查询取决于同一个表中的先前数据?

mysql - SQL硬查询: events between other events

php - 将值(value)从一列转移到另一列

php - 如何以html形式填充下拉菜单

mysql - 正确的 MySQL Left Join Query 会使小型数据库负担过重,尽管行已被索引

c++ - 是否可以仅使用过滤器处理程序来扩展 Windows 搜索索引?

mysql - BETWEEN 查询的性能较差

php - 登录脚本不工作,显示回显消息

c# - 数据库索引碎片 : Sequential GUID saved as String works fine but not saved as GUID

mysql - 更改 VARCHAR 字段长度后重新生成索引