【问题标题】:Do queries from subqueried tables get optimized?来自子查询表的查询是否得到优化?
【发布时间】:2014-09-25 00:11:20
【问题描述】:

关于查询优化,我想知道下面这样的语句是否得到优化:

select *
from (
    select *
    from table1 t1
    join table2 t2 using (entity_id)
    order by t2.sort_order, t1.name
)   as foo -- main query of object
where foo.name = ?; -- inserted

考虑到查询由依赖对象处理,但只是(对吗?)允许添加 WHERE 条件。我认为至少不会有很多数据被提取到你最喜欢的语言中,但如果这是一个充分的优化,我会重新考虑,也许数据库仍然需要一些时间来完成查询。

或者最好把那个查询拿出来,写一个单独的查询方法,里面有 where 可能还有一个 LIMIT 1 子句?

【问题讨论】:

  • 请不要不要发布像这样的 DBMS 特定问题作为一般跨 DBMS 问题。如果它不是特定于 DBMS 的,只需使用 SQL 标记。如果它是特定于 DBMS 的,选择一个标签 并使用它。当有一个用于 MySQL 和一个用于 PostgreSQL 时,您可以在此处将哪个答案标记为“正确”?
  • @CraigRinger:你应该投票以“太宽泛”结束
  • @Flimzy:投票关闭为“太宽泛”...在删除指定mysql的标签后
  • @spencer7593:这个问题有两个相互冲突的标签:mysql postgresql。
  • @Agi:有时这两个标签都适用......例如当询问如何在 mysql 的 postgres 中执行 X 时,反之亦然。一般规则是,每个问题都应该有一个具体的答案。您的问题需要两个答案。

标签: mysql sql postgresql sql-execution-plan


【解决方案1】:

MySQL 中,没有。

外部查询中的谓词不会“下推”到内联视图查询中。

首先处理内联视图中的查询,独立于外部查询。 (MySQL 将优化该视图查询,就像单独提交该查询一样优化该查询。)

MySQL 处理此查询的方式:首先运行内联视图查询,结果被具体化为“派生表”。也就是说,该查询的结果集在某些情况下作为临时表存储在内存中(如果它足够小,并且不包含 MEMORY 引擎不支持的任何列。否则,它会被拆分为使用 MyISAM 存储引擎作为 MyISAM 表的磁盘。

一旦派生表被填充,外部查询就会运行。

(请注意,派生表上没有有任何索引。在 5.6 之前的 MySQL 版本中确实如此;我认为 5.6 中有一些改进,其中 MySQL 将实际创建索引。

澄清:派生表上的索引:从 MySQL 5.6.3 开始,“在查询执行期间,优化器可能会向派生表添加索引以加快从中检索行的速度。”参考:http://dev.mysql.com/doc/refman/5.6/en/subquery-optimization.html

另外,我认为 MySQL 不会从内联视图中“优化”任何不需要的列。如果内联视图查询是SELECT *,则所有列都将在派生表中表示,无论这些列是否在外部查询中引用。

这可能会导致一些重大的性能问题,尤其是当我们不了解 MySQL 如何处理语句时。 (而且 MySQL 处理语句的方式与其他关系数据库(如 Oracle 和 SQL Server)明显不同。)

您可能听说过“避免在 MySQL 中使用视图”的建议。此一般建议(适用于“存储”视图和“内联”视图)背后的原因是可能会引入不必要的重大性能问题。

例如,对于这个查询:

SELECT q.name
  FROM ( SELECT h.*
           FROM huge_table h
       ) q
 WHERE q.id = 42

MySQL 不会将谓词id=42 向下“推送”到视图定义中。 MySQL 首先运行内联视图查询,并实质上创建了huge_table 的副本,作为未索引的 MyISAM 表。完成后,外部查询将扫描表的副本,以找到满足谓词的行。

如果我们改写查询以将谓词“推送”到视图定义中,如下所示:

SELECT q.name
  FROM ( SELECT h.*
           FROM huge_table h
          WHERE h.id = 42
       ) q

我们希望从视图查询返回的结果集要小得多,而派生表应该小得多。 MySQL 也将能够有效地利用索引ON huge_table (id)。但是仍然存在与实现派生表相关的一些开销。

如果我们从视图定义中消除不必要的列,那么效率会更高(特别是如果有很多列,有任何大列,或者任何具有 MEMORY 引擎不支持的数据类型的列):

SELECT q.name
  FROM ( SELECT h.name
           FROM huge_table h
          WHERE h.id = 42
       ) q

完全消除内联视图会更有效:

SELECT q.name
  FROM huge_table q
 WHERE q.id

【讨论】:

  • 哇,真的吗? MySQL 中没有限定符上拉/下推?
  • "MySQL 将实际创建一个索引"。我严重怀疑 MySQL 在查询执行期间会自动为物化表动态创建索引。
  • CraigRinger:没错。在 MySQL 中,谓词不会被推送到视图查询中,这与我们在其他数据库引擎中所期望的行为非常不同。 @ErwinBrandstetter:我以为我在某个地方看到了 MySQL 添加或正在添加(在 5.6、5.7 版中?或者可能是 MariaDB?)当优化器检测到它会提高性能时自动在派生表上创建索引的功能。 (我目前没有该参考资料。)我确信在 5.5 及更早版本中为派生表创建了 no 索引。
  • @ErwinBrandstetter:在 MySQL 5.6 参考手册http://dev.mysql.com/doc/refman/5.6/en/subquery-optimization.html 8.2.1.18.3 从 MySQL 5.6.3 开始...“在查询执行期间,优化器可能向派生表添加索引以加快从中检索行的速度。"
  • @spencer7593:当之无愧的赞成票。深层链接实际上回答了这个问题的大部分:dev.mysql.com/doc/refman/5.6/en/…
【解决方案2】:

我不能代表 MySQL - 更不用说它可能因存储引擎和 MySQL 版本而异,但对于 PostgreSQL:

PostgreSQL 会将其扁平化为单个查询。内部的ORDER BY 不是问题,因为添加或删除谓词不会影响其余行的顺序。

它会变平为:

select *
from table1 t1
join table2 t2 using (entity_id)
where foo.name = ?
order by t2.sort_order, t1.name;

然后连接谓词将在内部进行转换,生成与 SQL 对应的计划:

select t1.col1, t1.col2, ..., t2.col1, t2.col2, ...
from table1 t1, table2 t2 
where 
   t1.entity_id = t2.entity_id
   and foo.name = ?
order by t2.sort_order, t1.name;

带有简化架构的示例:

regress=> CREATE TABLE demo1 (id integer primary key, whatever integer not null);
CREATE TABLE
regress=> INSERT INTO demo1 (id, whatever) SELECT x, x FROM generate_series(1,100) x;
INSERT 0 100
regress=> EXPLAIN SELECT *
FROM (
    SELECT *
    FROM demo1
    ORDER BY id
) derived
WHERE whatever % 10 = 0;
                        QUERY PLAN                         
-----------------------------------------------------------
 Sort  (cost=2.51..2.51 rows=1 width=8)
   Sort Key: demo1.id
   ->  Seq Scan on demo1  (cost=0.00..2.50 rows=1 width=8)
         Filter: ((whatever % 10) = 0)
 Planning time: 0.173 ms
(5 rows)

...与以下计划相同:

EXPLAIN SELECT *
FROM demo1
WHERE whatever % 10 = 0
ORDER BY id;
                        QUERY PLAN                         
-----------------------------------------------------------
 Sort  (cost=2.51..2.51 rows=1 width=8)
   Sort Key: id
   ->  Seq Scan on demo1  (cost=0.00..2.50 rows=1 width=8)
         Filter: ((whatever % 10) = 0)
 Planning time: 0.159 ms
(5 rows)

如果在内部查询中有 LIMITOFFSET、窗口函数或某些其他阻止限定符下推/上拉/展平的东西,那么 PostgreSQL 会认识到它不能安全地展平它。它会通过具体化内部查询或迭代其输出并将其提供给外部查询来评估内部查询。

这同样适用于视图。 PostgreSQL 会在安全的情况下将视图内联和展​​平到包含查询中。

【讨论】:

  • +1。很好的解释。 (我对 Postgres 的经验很少,那是几年前的事了,但解释得很好。)
猜你喜欢
  • 1970-01-01
  • 2018-04-20
  • 2011-06-09
  • 1970-01-01
  • 2016-04-10
  • 2016-06-23
  • 1970-01-01
  • 1970-01-01
  • 2019-01-09
相关资源
最近更新 更多