这是一个缓慢的查询?可以改进吗?

时间:2013-02-19 20:30:42

标签: sql oracle query-optimization subquery correlated-subquery

我正在浏览SQLZOO“SELECT within SELECT tutorial”,这是完成工作的一个查询(任务 7

  

世界(名称,大陆,地区,人口,国内生产总值)

SELECT w1.name, w1.continent, w1.population 
FROM world w1
WHERE 25000000 >= ALL(SELECT w2.population FROM world w2 WHERE w2.continent=w1.continent)

我的问题是关于此类查询的有效性。子查询将针对主查询的每一行(国家/地区)运行,从而反复重新填充给定大陆的ALL列表。

  1. 我应该担心还是Oracle优化会以某种方式处理它?<​​/ li>
  2. 没有相关的子查询可以重新编程吗?

3 个答案:

答案 0 :(得分:3)

首先,您需要了解oracle如何转换此查询以进行评估。

SELECT w1.name
     , w1.continent
     , w1.population 
FROM world w1
WHERE 25000000 >= ALL(SELECT w2.population 
                       FROM world w2 
                      WHERE w2.continent=w1.continent
                     );

现在,优化器将使用ALL比较运算符后跟子查询的条件转换为使用ANY比较运算符和补充比较运算符的等效条件

  SELECT w1.name
        , w1.continent
       , w1.population 
  FROM world w1
   WHERE NOT(25000000 < ANY (SELECT w2.population 
                        FROM world w2 
                      WHERE w2.continent=w1.continent)
          );

然后,优化器会使用ANY比较运算符转换条件的规则进一步将第二个查询转换为以下查询,然后是相关子查询:

  SELECT w1.name
       , w1.continent
       , w1.population 
   FROM world w1
  WHERE
     NOT EXISTS (SELECT w2.population 
                  FROM world w2 
                 WHERE w2.continent=w1.continent
                   AND 25000000 < w2.population
                );

这是我从oracle来源Link

取的

对于你的问题:

  1. 是的,oracle会照顾这个,正如转换建议的那样,oracle如何转换上面的查询。但是更好地理解这个最终结果查询是如何工作的。
  2. 是的,这可以在没有相关子查询的情况下完成,但无论如何你必须加入同一个表,因为你需要比较具有相同大陆的表中的其他记录。[如果我需要纠正我我错了]

答案 1 :(得分:1)

您可以简化此操作,而无需扫描表格两次:

select a.name, a.continent, a.population, a.max_pop
  from (select w1.name, w1.continent, w1.population, 
               max(w1.population) over (partition by w1.continent) max_pop
          from world w1
       ) a 
where 25000000 >= a.max_pop;

答案 2 :(得分:1)

如果要在没有相关子查询的情况下重写查询,可以采用以下方法:

SELECT w1.name, w1.continent, w1.population 
FROM world w1
  JOIN
    ( SELECT continent, MAX(population) AS max_population
      FROM world
      GROUP BY continent
    ) c
    ON c.continent = w1.continent
WHERE 25000000 >= c.max_population ;

我并不暗示这会更快。 Oracle的优化器非常好,这是一个简单的整体查询,但是你编写它。这是另一种简化:

SELECT w1.name, w1.continent, w1.population 
FROM world w1
  JOIN
    ( SELECT continent
      FROM world
      GROUP BY continent
      HAVING MAX(population) <= 25000000 
    ) c
    ON c.continent = w1.continent ;