5

我正在阅读 SQLZOO“ SELECT 教程中的 SELECT ”,这是完成这项工作的查询之一(任务7

世界(名称、大陆、地区、人口、gdp)

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 优化会以某种方式处理它?
  2. 可以在没有相关子查询的情况下对其重新编程吗?
4

3 回答 3

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 源链接中获取的

对于您的问题:

  1. 是的,oracle 会处理这个问题,正如转换所暗示的那样,oracle 如何转换上述查询。但更好地理解这个最终结果查询是如何工作的。
  2. 是的,这可以在没有相关子查询的情况下完成,但无论如何你必须加入同一个表,因为你需要比较表中具有相同大陆的其他记录。[如果我错了,请纠正我]
于 2013-02-19T21:01:23.103 回答
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 ;
于 2013-02-20T08:23:57.767 回答
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;
于 2013-02-20T08:16:23.017 回答