2008-09-18 17 views
14

Tengo una tabla sencilla comenta (id INT, revision INT, comment VARCHAR(140)) con un cierto contenido de esta manera:Pregunta de mejor rendimiento para "seleccionar máximo en el grupo"?

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

estoy en busca de una instrucción SQL que devolverá cada comentario con la mayor revisión:

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

tengo llegar a esta solución:

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

pero es muy lento en grandes conjuntos de datos. ¿Hay mejores consultas para lograr esto?

+0

podrías considerar renombrar el tema para reflejar la optimización o el rendimiento? – hometoast

+0

El uso de funciones de ventana suele ser más rápido. –

Respuesta

6
  1. Asegúrese de tener sus índices configurados correctamente. Indización en id, la revisión sería buena.

  2. Aquí hay una opinión diferente sobre su consulta. no ha comprobado su plan de ejecución, pero si se configura el índice también debe ayudar:

    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 
    

Editted para añadir:

  1. Si' re en SQL Server, es posible que desee echa un vistazo a las vistas indizadas así:
    http://www.microsoft.com/technet/prodtechnol/sql/2005/impprfiv.mspx

Editted de nuevo para añadir información:

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% 

Todavía seguiría con la consulta secundaria.

+0

Si sus planes de ejecución intentan comparar la respuesta aceptada (utilizando la combinación externa izquierda) con la consulta de subconsulta/grupo en esta respuesta, está comparando en columnas no indexadas. Con los índices correctos, la combinación externa izquierda será más efectiva casi siempre (especialmente cuando tiene muchos registros). Esta respuesta es perfectamente aceptable para una cantidad muy limitada de registros, pero cuando llega a 10K + registros, encontrará mejores resultados con la combinación externa. –

11

Ésta es una forma de que con la indexación adecuada no será atrozmente lento y que no utiliza una subselección:

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 

Adaptado de consultas aquí: http://www.xaprb.com/blog/2007/03/14/how-to-find-the-max-row-per-group-in-sql-without-subqueries/

(búsqueda de Google: Grupo máximo por sql)

4

Probado con una de nuestras tablas que tiene casi 1 millón de filas en total. Los índices existen en ambos campos FIELD2 Y FIELD3. Query devolvió 83953 filas en menos de 3 segundos en nuestro cuadro 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 
0

Idea del campo de la izquierda, pero ¿qué pasa con la adición de un campo adicional a la tabla:

CurrentRevision bit not null 

Luego, cuando se hace un cambio, establecer el indicador en la nueva revisión y quitarlo de todas las anteriores unos.

Su consulta sería entonces simplemente se convierten en:

select Id, 
     Comment 
from Comments 
where CurrentRevision = 1 

Esto sería mucho más fácil en la base de datos y por lo tanto mucho más rápido.

0

Una forma bastante clara de hacer las consultas de tipo "latest x by id" es esto.También debería ser bastante fácil de indexar correctamente.

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

Para tablas grandes me parece que esta solución puede tiene un mejor rendimiento:

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 
1

Analytics sería mi recomendación.

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

Sin subselects (o tablas temporales):

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 
Cuestiones relacionadas