我同意 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