来自subqueried表的查询是否得到优化?

时间:2014-09-25 00:11:21

标签: mysql sql postgresql sql-execution-plan

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

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条件中处理。我认为至少没有很多数据被引入你最喜欢的语言,但如果这是一个充分的优化并且数据库仍然需要花费一些时间,我还有第二个想法。通过查询。

或者最好是把那个查询带出来并编写一个单独的查询方法,该方法也有一个LIMIT 1子句?

2 个答案:

答案 0 :(得分:4)

MySQL 中,没有。

外部查询中的谓词得到"推送"进入内联视图查询。

首先处理内联视图中的查询,与外部查询无关。 (如果您单独提交,MySQL将优化该视图查询,就像优化该查询一样。)

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

填充派生表后,外部查询将运行。

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

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

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

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

您可能听到过建议"避免在MySQL"中使用视图。这个一般性建议(适用于"存储"视图和"内联和#34;视图)背后的原因是可能不必要地引入的重大性能问题。

例如,对于此查询:

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

MySQL没有"推送"谓词id=42进入视图定义。 MySQL首先运行内联视图查询,并基本上创建huge_table的副本,作为未索引的MyISAM表。完成后,外部查询将扫描表的副本,以找到满足谓词的行。

如果我们改为将查询重新写入" push"谓词定义到视图定义中,如下所示:

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

答案 1 :(得分:4)

我不能代表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会认识到它可以& #39;安全地压扁它。它通过实现内部查询或通过迭代其输出并将其提供给外部查询来评估内部查询。

同样适用于视图。 PostgreSQL将对包含查询的内容进行内联和展平,以确保安全。