Замена коррелированного подзапроса только на JOIN(s)?

Этот вопрос является продолжением моего предыдущего вопроса . Он также основан на учебнике SQLZOO "SELECT в SELECT". На этот раз речь пойдет об задаче № 8.

Во-первых, приемлемое решение:

SELECT w1.name, w1.continent FROM world w1
WHERE w1.population > ALL(
SELECT w2.population*3 FROM world w2
WHERE w2.continent=w1.continent and w2.name<>w1.name)

На этот раз коррелированный запрос глубоко укоренился в логике.

Можно ли по-прежнему разумно переписать этот запрос только с помощью JOIN? Должно ли это быть?


person PM 77-1    schedule 21.02.2013    source источник


Ответы (2)


Итак, задача 8 гласит: В некоторых странах население более чем в три раза больше, чем в любой из их соседей (на том же континенте). Дайте страны и континенты.

Я нахожу это немного неясным. Какие соседи? Это все страны континента? Если да, то следующий вопрос заключается в том, сравниваем ли мы с наименьшим соседом или с наибольшим соседом. Допустим, это самый большой сосед. Тогда запрос будет примерно таким

select name, continent
  from world w1
 where w1.population > (select max(3*w2.population) 
                          from world w2
                         where w2.continent =  w1.continent
                           and w2.name      <> w1.name)

В противном случае, если бы это было 3 раза всех соседей вместе, это было бы это

select name, continent
  from world w1
 where w1.population > (select 3*sum(w2.population) 
                          from world w2
                         where w2.continent =  w1.continent
                           and w2.name      <> w1.name)

Надеюсь, поможет.

Изменить: в руководстве по Oracle SQL (E26088-01) говорится о ВСЕХ функциях:

Сравнивает значение с каждым значением в списке или возвращенным запросом.

Пример:

SELECT * FROM employees
WHERE salary >=
ALL ( 1400, 3000)
ORDER BY employee_id;

Сначала агрегируя, а затем сравнивая, мы сводим его к одному сравнению. Результат в приведенной таблице примера тот же. Но запрос по ВСЕМ странам может на самом деле дать другой результат. Так что в конце концов я должен сказать, что ваш запрос лучше. Если на континенте есть страна с населением 1 миллион, 3,5 миллиона и 11 миллионов, то на самом деле 2-я страна в 3 раза больше первой, а 3-я страна в 3 раза больше 2-й. Мой запрос будет сравнивать только 2-ю страну с 3-й, но ваш запрос также сравнивает первую со второй.

person hol    schedule 21.02.2013
comment
Это 1-й случай: страны, которые как минимум в 3 раза более населены, чем все остальные отдельные страны на том же континенте. Считаете ли вы, что ваш запрос лучше (то есть эффективнее) моего? - person PM 77-1; 21.02.2013
comment
Минимально эффективнее наверное. Я отредактирую пост, чтобы прояснить этот момент. - person hol; 21.02.2013

Для этого конкретного запроса вам нужны самые большие и вторые по величине значения на каждом континенте. Я бы подошел к этому с оконными функциями:

select w.continent,
       max(case when seqnum = 1 then w.name end) as name
from (select w.*, 
             row_number() over (partition by continent order by population desc) as seqnum
      from world w
     ) w
where seqnum in (1, 2)
group by continent
having max(case when seqnum = 1 then population end) > 3*max(case when seqnum = 2 then population end)

Хорошо, я признаю, что это, вероятно, немного продвинуто, учитывая источник вопроса.

Итак, вот как вы можете сделать это, используя соединения вместо коррелированных подзапросов:

Я думаю, что следующая ваша логика:

select w.name, w.continent
from world w join
     (SELECT w.continent, maxpopulation,
             max(case when population <> maxpopulation then population end) as secondmax
      FROM world w join
           (select continent, max(population) as maxpopulation
            from world
            group by continent
           ) c
           on w.continent = c.continent
      group by w.continent, maxpopulation
     ) wc
     on w.continent = wc.continent and w.population = maxpopulation
where population >= 3*secondmax

Таким образом, ответ «да, вы можете сделать это без коррелированных подзапросов». В большинстве распространенных случаев вы можете переписать коррелированные подзапросы, используя соединения и группировки.

person Gordon Linoff    schedule 21.02.2013
comment
В качестве примечания. . . Я полностью переписал ответ после этого комментария. - person Gordon Linoff; 21.02.2013
comment
Большое спасибо. Очень интересно. Ваш 1-й запрос работает по назначению. Oracle, однако, жалуется на второй. Я не смог найти исправление. Когда у вас будет минутка, не могли бы вы вернуться к нему? - person PM 77-1; 22.02.2013