我正在做一个基于JavaEE的MySql数据库的web项目.我们需要一个视图来总结3个表中总计超过300万行的数据.每个表都是用索引创建的.但我还没有找到一种方法来利用我们用[group by]创建的视图中的条件select语句检索中的索引.

我从using views in MySql is not a good idea人那里得到了一些建议.因为你不能像oracle那样在mysql中为视图创建索引.但在我做的一些测试中,索引可以用在view select语句中.也许我以错误的方式创建了这些视图.

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

我们有一个记录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);

然后我创建了一个视图来查看科比·布莱恩特每年的最高得分

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;

我写了一份有条件的声明,让kobe分在2006分中获得最高分数;

select * from v_kobe_highscores where happened_in = 2006;

当我在toad for mysql中解释它时,我发现mysql已经扫描了all rows来形成视图,然后在其中查找带有条件的数据,而不使用[Occessed_in]上的索引.

explain select * from v_kobe_highscores where happened_in = 2006;

解释结果

我们在项目中使用的视图是在具有数百万行的表中构建的.在每个视图数据检索中扫描表中的所有行是不可接受的.请帮忙!谢谢

@虫族这是我在现实生活中测试的结果.我看不出他们之间有什么不同.我认为@spencer7593的观点是正确的.The MySQL optimizer doesn't "push" that predicate down in the view query.

推荐答案

如何让MySQL使用索引进行视图查询?简而言之,提供一个MySQL可以使用的索引.

在这种情况下,最佳指数可能是"覆盖"指数:

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

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

这是一个快速的答案.较长的答案是,MySQL不太可能在视图查询中使用前导列为happened_id的索引.


为什么视图会导致性能问题

MySQL视图的一个问题是,MySQL不会将谓词从外部查询"推"到视图查询中.

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

底线是,在happened_in上定义的索引在构成视图定义的查询中没有被MySQL使用.

创建中间"派生表"后,将使用该"派生表"作为行源执行外部查询.当外部查询运行时,将计算happened_in = 2006谓词.

请注意,视图查询中的所有行都是存储的,在您的例子中,每happened_in个值对应一行,而不仅仅是在外部查询中指定相等谓词的行.

视图查询的处理方式在某些人看来可能是"意外的",这也是与其他关系数据库处理视图查询的方式相比,在MySQL中使用"视图"可能会导致性能问题的原因之一.


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

考虑到视图定义和查询,对于视图查询,最好的方法是"使用索引"访问方法.要得到它,你需要一个覆盖指数,例如.

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

这可能是对现有视图定义和现有查询最有利的索引(性能方面).player列是前导列,因为在视图查询中该列上有一个相等谓词.下一个是happened_in列,因为在该列上有一个groupby操作,MySQL将能够使用这个索引来优化groupby操作.我们还包括score列,因为这是查询中引用的唯一其他列.这使得索引成为"覆盖"索引,因为MySQL可以直接从索引页面满足查询,而不需要访问基础表中的任何页面.这就是我们将从查询计划中得到的好处:"使用索引",而不是"使用文件排序".


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

您可以将查询的执行计划与视图与等效的独立查询进行比较:

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使用索引进行视图查询

答:定义视图查询可以使用的适当索引

简单的回答是提供一个"覆盖索引"(索引包括视图查询中引用的所有列).该索引中的前导列应该是用相等谓词引用的列(在您的例子中,列player将是一个前导列,因为查询中有一个player = 24谓词.此外,GROUP BY中引用的列应该是索引中的前导列,这允许MySQL通过使用索引而不是使用排序操作来优化GROUP BY操作.).

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

在MySQL中使用视图并不一定是一个"坏主意",但我要强烈提醒那些 Select 在MySQL中使用视图的人,要知道MySQL是如何处理引用这些视图的查询的.MySQL处理视图查询的方式与其他数据库(如Oracle、SQL Server)处理视图查询的方式(显著)不同.

Mysql相关问答推荐

MySQL问题难以将文本字符串转换为正确的日期格式

不分组寻呼

MySQL - 密码哈希没有预期的格式

列出每年最多产的三位作家

按优先级 for each 具有 status_id 的员工 Select 单行

关联同一个表中的两列

如何在 MySQL 中查找重复值和更新值

插入二进制数据会导致Data too long for column...

SQL从具有相同列的行中 Select 最后一行

查找同时玩过这两种游戏的玩家

Mysql 相等性反对 false 或 true

如何通过未知列中的唯一值有效地更新 MySQL 行

如何使用 laravel 连接 mysql?

减少mysql中的值但不是负数

MySQL Workbench - 如何同步 EER 图

MySQL:低基数/ Select 性列=如何索引?

从 MySQL JSON 数据类型中提取不带引号的值

如何将具有相同列值的mysql行分组为一行?

数据截断:第 1 行的logo列数据太长

按 COUNT(*) 过滤?