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

时间:2014-03-20 20:20:34

标签: mysql sql join view union-all

假设我在 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

经常开始出现在其他查询中:作为加入子选择的一部分。 目前,我只是将此查询注入加入子选择,如:

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

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

由于我从未体验过观点,并且听说他们有许多缺点,我的问题是:

为讨论的联合查询创建视图是否正常,并在我的加入子选择等中使用它?在性能方面 - 与将其插入连接子选择等相比,它会更糟,更平等还是更好?在这种情况下,有视图有任何缺点吗?

提前感谢您的帮助!

2 个答案:

答案 0 :(得分:4)

我同意Bill Karwin的优秀答案中的所有观点。

问:为讨论的联合查询创建视图并在我的联接,子选择等中使用它是否正常?

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

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

A:引用视图对象的效果与等效的内联视图相同。

(查看视图对象,检查权限,然后用存储的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正在使用它时,该名称很有意义。


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

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

然后,有人出现并决定他们将创建新视图,该视图引用旧视图。他只需要一个行的子集,并且不能修改现有的视图,因为这可能会破坏某些东西,所以他创建了一个新的视图......从公共视图创建视图myview p WHERE p.col ='foo'。

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

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

至少在内联视图中,编写SQL语句的人更了解正在执行的实际SQL,并且所有SQL布局都提供了调整性能的机会。

我的两分钱。

TAMING BEASTLY 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

答案 1 :(得分:3)

视图会缩短您的SQL。就是这样。

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

实际上,视图可能会导致更差性能,具体取决于查询以及您如何使用它们,因为每次查询时,它们可能需要将结果存储在临时表中< / em>的

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

另一方面,UNION查询还会在累积结果时创建临时表。所以无论如何你都要承担临时表的费用。