结合 UNION ALL 的表的 VIEW 的 MySQL 性能

Posted

技术标签:

【中文标题】结合 UNION ALL 的表的 VIEW 的 MySQL 性能【英文标题】:MySQL performance of VIEW for tables combined with UNION ALL 【发布时间】:2014-03-20 20:20:33 【问题描述】:

假设我在 MySQL 中有 2 个表:

create table `persons` (
    `id` bigint unsigned not null auto_increment,

    `first_name` varchar(64),
    `surname` varchar(64),

    primary key(`id`)
);

create table `companies` (
    `id` bigint unsigned not null auto_increment,

    `name` varchar(128),

    primary key(`id`)
);

现在,我经常需要对它们一视同仁,这就是以下查询的原因:

select person.id as `id`, concat(person.first_name, ' ', person.surname) as `name`, 'person' as `person_type`
from persons
union all
select company.id as `id`, company.name as `name`, 'company' as `person_type`
from companies

开始经常出现在其他查询中:作为 joinssubselects 的一部分。 现在,我只是将此查询注入到 joinssubselects 中,例如:

select *
from some_table row
     left outer join (>>> query from above goes here <<<) as `persons`
     on row.person_id = persons.id and row.person_type = persons.person_type

但是,今天我不得不将讨论过的联合查询多次用于另一个查询,即加入两次。

由于我从未体验过视图,并且听说它们有很多缺点,所以我的问题是:

为讨论的联合查询创建一个 view 并在我的 joinssubselects 等中使用它是正常的做法吗?在性能方面 - 与仅将其插入 joinssubselects 等相比,它会更差、相等还是更好?在这种情况下,拥有 view 有什么缺点吗?

提前感谢您的帮助!

【问题讨论】:

【参考方案1】:

我同意 Bill Karwin 出色回答中的所有观点。

问:为讨论的联合查询创建视图并将其用于我的联接、子选择等是否是正常做法?

答:对于 mysql,更正常的做法是避免使用“CREATE VIEW”语句。

问:在性能方面 - 与仅将其插入连接、子选择等相比,它会更差、相等还是更好?

答:引用视图对象将具有与等效内联视图相同的性能。

(查找视图对象,检查权限,然后用存储的 SQL 替换视图引用,与发送仅稍长一点的语句相比,可能需要做更多的工作。但是这些差异中的任何一个都是微不足道的。)

问:在这种情况下有视图有什么缺点吗?

答:最大的缺点是 MySQL 处理视图的方式,无论是存储的还是内联的。 MySQL 将始终运行视图查询并将该查询的结果具体化为临时 MyISAM 表。但是视图定义是否被存储,或者它是否被包含在内并没有区别。 (其他 RDBMS 处理视图的方式与 MySQL 大不相同)。

视图的一大缺点是来自外部查询的谓词永远不会下推到视图查询中。每次引用该视图时,即使查询单个 id 值,MySQL 也会运行视图查询并创建一个临时 MyISAM 表(上面没有索引),然后 MySQL 将针对该临时表运行外部查询MyISAM 表。

因此,就性能而言,请考虑引用与“CREATE TEMPORARY TABLE t (cols) ENGINE=MyISAM”和“INSERT INTO t (cols) SELECT ...”相当的视图。

MySQL 实际上将内联视图称为“派生表”,当我们了解 MySQL 正在用它做什么时,这个名称就很有意义。


我个人的偏好是不使用“CREATE VIEW”语句。最大的缺点(如我所见)是它“隐藏”了正在执行的 SQL。对于未来的读者,对视图的引用看起来像一个表格。然后,当他去写一条SQL语句时,他会像引用一张表一样引用视图,非常方便。然后他决定他要将该表连接到它自己,并带有另一个引用。 (对于第二个参考,MySQL 也再次运行该查询,并创建另一个临时(和未索引)MyISAM 表。现在有一个 JOIN 操作。然后添加谓词“WHERE view.column = 'foo'”在外部查询上。

它最终“隐藏”了最明显的性能改进,将该谓词滑入视图查询中。

然后,有人出现并决定他们要创建引用旧视图的新视图。他只需要行的子集,并且不能修改现有视图,因为这可能会破坏某些内容,因此他创建了一个新视图... CREATE VIEW myview FROM publicview p WHERE p.col = 'foo'。

现在,对 myview 的引用将首先运行 publicview 查询,创建一个临时 MyISAM 表,然后针对该表运行 myview 查询,创建另一个临时 MyISAM 表,外部查询将针对该表运行.

基本上,视图的便利性可能会导致无意的性能问题。有了数据库上可供任何人使用的视图定义,就会有人使用它,即使它不是最合适的解决方案。

至少使用内联视图,编写 SQL 语句的人更清楚正在执行的实际 SQL,并且将所有 SQL 布置好提供了调整它以提高性能的机会。

我的两分钱。

驯服野兽 SQL

我发现应用常规格式规则(我的工具会自动执行)可以将可怕的 SQL 转变为我可以阅读和使用的东西。

SELECT row.col1
     , row.col2
     , person.*
  FROM some_table row
  LEFT
  JOIN ( SELECT 'person'  AS `person_type`
              , p.id      AS `id`
              , CONCAT(p.first_name,' ',p.surname) AS `name`
           FROM person p
          UNION ALL
         SELECT 'company' AS `person_type`
              , c.id      AS `id`
              , c.name    AS `name`
           FROM company c
       ) person
    ON person.id = row.person_id
   AND person.person_type = row.person_type

我同样可能完全避免使用内联视图,并在 SELECT 列表中使用条件表达式,尽管这对于很多列来说确实变得更加笨拙。

SELECT row.col1
     , row.col2
     , row.person_type AS ref_person_type
     , row.person_id   AS ref_person_id
     , CASE
       WHEN row.person_type = 'person'  THEN p.id 
       WHEN row.person_type = 'company' THEN c.id
       END AS `person_id`
     , CASE
       WHEN row.person_type = 'person'  THEN CONCAT(p.first_name,' ',p.surname)
       WHEN row.person_type = 'company' THEN c.name
       END AS `name`
  FROM some_table row
  LEFT
  JOIN person p
    ON row.person_type = 'person'
   AND p.id = row.person_id
  LEFT
  JOIN company c
    ON row.person_type = 'company'
   AND c.id = row.person_id

【讨论】:

@spencer7583 感谢您的扩展回答 - 它说服了我内联选择而不是将它们移动到视图中。不知何故,我开始认为视图会优化整体性能,因为它们会缩短 SQL,但看起来这是一种误解。 @Yura:是的,MySQL 中的视图就是这样骗人的。起初,它们看起来是个好主意,但结果却是个坏主意。 (我认为 MySQL 增加对视图的支持的主要原因是可以将其他数据库的“CREATE VIEW”语法导入 MySQL。) @spencer5793 还好我没有陷入VIEWS的陷阱 :) 我开始考虑VIEWS的原因是因为我的SQL语句开始增长,但据我了解:1) VIEWS 并不能解决这个问题 2) 查询大小的增加并没有那么大 @Yura:大型 SQL 语句没有理由变得如此糟糕。用格式化、格式化、格式化来驯服它们。使用短的行源别名,使用别名限定所有列引用、堆栈和缩进、堆栈和缩进,并使关键字和括号对齐...便于突出显示内联视图查询。【参考方案2】:

视图使您的 SQL 更短。就是这样。

对于查看存储任何内容的 MySQL 用户来说,这是一个常见的误解。他们没有(至少在 MySQL 中没有)。它们更像是别名或宏。查询视图通常就像以“扩展”形式运行查询一样。在一个查询中查询一个视图两次(如您提到的连接示例中)不会利用视图的任何优势——它将运行两次查询。

事实上,视图会导致更差的性能,这取决于查询以及您如何使用它们,因为它们可能需要将结果存储在一个临时表中您每次查询它们时。

请参阅http://dev.mysql.com/doc/refman/5.6/en/view-algorithms.html,了解有关视图何时使用临时算法的更多详细信息。

另一方面,UNION 查询在累积结果时也会创建临时表。所以无论如何你都被临时表的成本所困扰。

【讨论】:

。 . MySQL为union all查询创建一个临时表真的是真的吗?这似乎非常低效。 视图不仅使 SQL 更短,而且还“隐藏”了 SQL 的重要部分。 MySQL 不会将外部查询中的谓词“推送”到视图查询(内联或存储)。如果外部查询有一个也适用于视图查询的谓词,那么在查看查询。 @GordonLinoff,是的,UNION ALL 需要一个临时表。他们终于在 MySQL 5.7.3 中修复了它(在某些情况下),请参阅bugs.mysql.com/bug.php?id=50674 @BillKarwin 非常感谢您的解释!当 spencer7593 的一个人出来时,我正要接受你的回答,在阅读了他的回答后,我改变了对观点的看法:)

以上是关于结合 UNION ALL 的表的 VIEW 的 MySQL 性能的主要内容,如果未能解决你的问题,请参考以下文章

union all 效率问题

UNION ALL UNION

SQL Server 2012 上的可插入 UNION ALL VIEW

MySQL - 使用 UNION ALL 获取错误的表字段但结果是正确的

使用Union All 和Order By In View?

如何查询拆分为 2 个较小表的表?联盟?看法?