Каковы преимущества левого внешнего соединения по сравнению с вложенными выборками агрегатов, чтобы найти самые новые строки в таблице? - PullRequest
2 голосов
/ 03 мая 2011

Я делаю:

select * from mytable y
where y.year = (select max(yi.year) 
                from mytable yi
                where yi.person = y.person)

Это лучше или хуже с точки зрения производительности, чем:

select y.* from mytable y
left outer join mytable y2
  on y.year < y2.year
  and y.person = y2.person
where y2.year is null

План объяснения / анекдотические доказательства неубедительны, поэтому мне интересно,в общем одно лучше другого.

Ответы [ 3 ]

5 голосов
/ 03 мая 2011

«В общем», оба запроса могут создавать разные планы выполнения в зависимости от распределения данных.

Однако, если предположить, что ваш второй запрос действительно такой:

SELECT  y.*
FROM    mytable y
LEFT JOIN
        mytable y2
ON      y2.person = y.person
        AND y2.year > y.year
WHERE   y2.year IS NULL

, *Версия 1006 *, скорее всего, будет быстрее, так как она будет оптимизирована либо до HASH ANTI JOIN, либо HASH JOIN с фильтром, в зависимости от того, есть у вас индекс на mytable (person, year) или нет, а также от некоторых других условий.Версия подзапроса не может быть оптимизирована для анти-объединения.

Скорее всего, эти запросы окажутся более эффективными:

SELECT  *
FROM    mytable y
WHERE   (y.person , y.year) IN
        (
        SELECT  person, MAX(year)
        FROM    mytable
        GROUP BY
                person
        )

или

SELECT  *
FROM    (
        SELECT  y.*,
                DENSE_RANK() OVER (PARTITION BY person ORDER BY year DESC) dr
        FROM    mytable y
        )
WHERE   dr = 1

с первымодин из них более эффективен в случае нескольких человек и нескольких лет на человека, а второй более эффективен в противоположном случае.

Вы можете заменить DENSE_RANK на ROW_NUMBER, что позволит вам получитьизбавьтесь от дубликатов на person, MAX(year), если захотите.

1 голос
/ 29 июня 2011

Как и у Адама ... Из того, что я прочитал, намного быстрее, чем любая версия, которая использует соединения.

select *
from (select person, year, ...
      Rank() over (partition by person order by year desc) as Rank
      from mytable)
where Rank = 1
1 голос
/ 03 мая 2011

Они не выглядят равнозначно - первый выполняет коррелированный подзапрос о человеке, которого нет во втором.

select * from mytable y
 where y.year = (select max(yi.year) 
                   from mytable yi
                  where yi.person = y.person)

Может быть переписано как:

select [list of columns] 
  from (select [list of columns including year], 
               max(year) over (partition by person) as max_year
          from mytable)
 where year = max_year

Чтобы избежать самостоятельного присоединения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...