假设我在和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
开始经常出现在其他查询中:作为的一部分,加入或子选择。
现在,我只是简单地将此查询注入(inject)联接或子选择中,例如:
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 并在我的中加入,子选择等,这是正常的做法吗?就性能而言-与仅将其插入加入,子选择等相比,会更差,相等还是更好?在这种情况下,使用 View 有什么缺点吗?
在此先感谢您的帮助!
最佳答案
我同意Bill Karwin的出色回答中的所有观点。
问:是否正常创建用于讨论的联合查询的 View 并将其用于我的联接,子选择等中?
答:对于MySQL,更常见的做法是避免使用“CREATE VIEW”语句。
问:在性能方面-与仅将其插入联接,子选择等相比,会更糟,是否相等或更好?
答:引用 View 对象将具有与等效的嵌入式 View 相同的性能。
(查找 View 对象,检查特权,然后用存储的SQL替换 View 引用可能要花更多的时间,而发送的语句要长得多。差异微不足道。)
问:在这种情况下,有 View 有任何缺点吗?
答:最大的缺点是MySQL如何处理 View (无论是存储 View 还是内联 View )。 MySQL将始终运行 View 查询并将该查询的结果具体化为临时MyISAM表。但是无论是存储 View 定义还是内联 View 都没有区别。 (其他RDBMS处理 View 的方式与MySQL截然不同)。
View 的一大缺点是,外部查询的谓词永远不会下推到 View 查询中。每次引用该 View 时,即使使用单个id值查询,MySQL都将运行该 View 查询并创建一个临时MyISAM表(上面没有索引),然后MySQL将对该临时表运行外部查询MyISAM表。
因此,就性能而言,请考虑对与“CREATE TEMPORARY TABLE t (cols) ENGINE=MyISAM
”和“INSERT INTO t (cols) SELECT ...
”相提并论的 View 的引用。
MySQL实际上将内联 View 称为“派生表”,当我们了解MySQL在做什么时,该名称很有意义。
我个人的喜好是不要使用“CREATE VIEW”语句。最大的缺点(如我所见)是它“隐藏”了正在执行的SQL。对于将来的读者来说,对该 View 的引用就像一张表。然后,当他去编写SQL语句时,他将像表一样引用 View ,因此非常方便。然后,他决定要将该表与自身连接,并对其进行另一个引用。 (作为第二个引用,MySQL还再次运行该查询,并创建了另一个临时(且未建立索引)MyISAM表。现在,在该表上执行了JOIN操作。然后添加了谓词“WHERE view.column ='foo'”在外部查询上。
最后,它“隐藏”了最明显的性能改进,使谓词滑入 View 查询。
然后,有人来决定他们要创建引用旧 View 的新 View 。他只需要一部分行,并且不能修改现有 View ,因为这可能会破坏某些内容,因此他创建了一个新 View ...从publicview创建 View myview p在哪里p.col ='foo'。
并且,现在,对myview的引用将首先运行publicview查询,创建一个临时MyISAM表,然后针对该myview查询运行,创建另一个临时MyISAM表,外部查询将针对该表运行。
基本上, View 的便利性可能会导致意外的性能问题。数据库中的 View 定义可供任何人使用,即使不是最合适的解决方案,也将有人使用。
至少在内联 View 中,编写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
我同样有可能完全避免使用内联 View ,并在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
关于结合UNION ALL的表的VIEW的MySQL性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22544181/