Самый эффективный запрос для "select max in group"?

У меня есть простая таблица комментариев (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
  );

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

Ответ 1

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

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 search: max group by sql)

Ответ 2

  • Убедитесь, что ваши индексы настроены соответствующим образом. Индексирование по id, ревизия будет хорошей.

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

    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%

Я все равно поеду с суб-запросом.

Ответ 3

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

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

Ответ 4

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

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;

Ответ 5

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

CurrentRevision bit not null

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

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

select  Id,
        Comment
from    Comments
where   CurrentRevision = 1

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

Ответ 6

Один довольно чистый способ делать запросы "самый последний x по 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
)

Ответ 7

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

    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

Ответ 8

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

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