Наилучший запрос для "выберите максимум в группе"? - PullRequest
14 голосов
/ 18 сентября 2008

У меня есть простая таблица комментариев (id INT, revision INT, comment VARCHAR(140)) с некоторым содержанием, подобным этому:

1|1|hallo1|
1|2|hallo2|
1|3|hallo3|
2|1|hallo1|
2|2|hallo2|

Я ищу оператор SQL, который будет возвращать каждый комментарий с самой высокой ревизией:

1|3|hallo3|
2|2|hallo2|

Я придумал это решение:

select id, revision, comment 
  from comments 
  where revision = (
      select max(revision) 
        from comments as f 
        where f.id = comments.id
  );

но это очень медленно на больших наборах данных. Есть ли лучшие запросы для достижения этой цели?

Ответы [ 8 ]

11 голосов
/ 18 сентября 2008

Вот один из способов, что при соответствующей индексации не будет ужасно медленным, и он не использует подвыбор:

SELECT comments.ID, comments.revision, comments.comment FROM comments 
LEFT OUTER JOIN comments AS maxcomments 
ON maxcomments.ID= comments.ID
AND maxcomments.revision > comments.revision
WHERE maxcomments.revision IS NULL

Адаптировано из запросов здесь: http://www.xaprb.com/blog/2007/03/14/how-to-find-the-max-row-per-group-in-sql-without-subqueries/

(из поиска Google: максимальная группа по sql)

6 голосов
/ 18 сентября 2008
  1. Убедитесь, что ваши индексы настроены правильно. Индексирование по id, ревизия была бы хороша.

  2. Вот другой взгляд на ваш запрос. Не проверяли план выполнения, но если вы правильно настроили индекс, это должно помочь:

    SELECT c.* 
      FROM comments c
      INNER JOIN (
            SELECT id,max(revision) AS maxrev 
              FROM comments 
              GROUP BY id
      ) b
        ON c.id=b.id AND c.revision=b.maxrev
    

Отредактировано, чтобы добавить:

  1. Если вы работаете на SQL Server, вы также можете проверить индексированные представления:
    http://www.microsoft.com/technet/prodtechnol/sql/2005/impprfiv.mspx

Снова отредактировано для добавления информации:

Subquery:
25157 records
2 seconds
Execution plan includes an Index Seek (82%) base and a Segment (17%)

Left Outer Join:
25160 records
3 seconds
Execution plan includes two Index Scans @ 22% each with a Right Outer Merge at 45% and a Filter at 11%

Я бы все-таки пошел с подзапросом.

4 голосов
/ 18 сентября 2008

Протестировано с одной из наших таблиц, которая содержит около 1 миллиона строк. Индексы существуют в обоих полях FIELD2 и FIELD3. Запрос вернул 83953 строк менее чем за 3 секунды в нашем окне разработки.

select
FIELD1, FIELD2, FIELD3
from
OURTABLE (nolock) T1
WHERE FIELD3 = 
(
SELECT MAX(FIELD3) FROM 
OURTABLE T2 (nolock)
WHERE T1.FIELD2=T2.FIELD2
)
ORDER BY FIELD2 DESC
1 голос
/ 22 сентября 2008

Аналитика будет моей рекомендацией.

select id, max_revision, comment
from (select c.id, c.comment, c.revision, max(c.revision)over(partition by c.id) as max_revision
      from comments c)
where revision = max_revision;
0 голосов
/ 23 июля 2012

Без подвыборов (или временных таблиц):

SELECT c1.ID, c1.revision, c1.comment 
FROM comments AS c1
LEFT JOIN comments AS c2 
    ON c1.ID = c2.ID
    AND c1.revision < c2.revision
WHERE c2.revision IS NULL
0 голосов
/ 21 сентября 2008

Для больших таблиц я считаю, что это решение может иметь лучшую производительность:

    SELECT c1.id, 
           c1.revision, 
           c1.comment 
      FROM comments c1 
INNER JOIN ( SELECT id, 
                max(revision) AS max_revision
               FROM comments 
           GROUP BY id ) c2
        ON c1.id = c2.id
       AND c1.revision = c2.max_revision
0 голосов
/ 19 сентября 2008

Один из самых простых способов выполнения запросов типа "самый последний x by id" заключается в следующем. Также должно быть довольно легко правильно индексировать.

SELECT id, revision, comment 
FROM comments
WHERE (id, revision) IN (
  SELECT id, MAX(revision)
  FROM comments
  -- WHERE clause comes here if needed
  GROUP BY id
)
0 голосов
/ 19 сентября 2008

Идея из левого поля, но как насчет добавления дополнительного поля в таблицу:

CurrentRevision bit not null

Затем, когда вы вносите изменения, установите флаг для новой ревизии и удалите ее для всех предыдущих.

Тогда ваш запрос просто станет:

select  Id,
        Comment
from    Comments
where   CurrentRevision = 1

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

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