Aquí se pueden encontrar muchas preguntas similares, pero no creo que ninguna responda la pregunta adecuadamente.
Continuaré con la pregunta más popular actual y usaré su ejemplo si está bien.
La tarea en este caso es obtener la última publicación para cada autor en la base de datos.
La consulta de ejemplo produce resultados inutilizables ya que no siempre es la última publicación que se devuelve.
SELECT wp_posts.* FROM wp_posts
WHERE wp_posts.post_status='publish'
AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author
ORDER BY wp_posts.post_date DESC
La respuesta actual aceptada es
SELECT
wp_posts.*
FROM wp_posts
WHERE
wp_posts.post_status='publish'
AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author
HAVING wp_posts.post_date = MAX(wp_posts.post_date) <- ONLY THE LAST POST FOR EACH AUTHOR
ORDER BY wp_posts.post_date DESC
Lamentablemente, esta respuesta es sencilla y errónea, y en muchos casos produce resultados menos estables que la consulta original.
Mi mejor solución es usar una subconsulta del formulario
SELECT wp_posts.* FROM
(
SELECT *
FROM wp_posts
ORDER BY wp_posts.post_date DESC
) AS wp_posts
WHERE wp_posts.post_status='publish'
AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author
Mi pregunta es simple: ¿hay alguna forma de ordenar filas antes de agrupar sin recurrir a una subconsulta?
Editar : Esta pregunta fue una continuación de otra pregunta y los detalles de mi situación son ligeramente diferentes. Puede (y debe) asumir que también hay un wp_posts.id que es un identificador único para esa publicación en particular.
fuente
post_author
ypost_date
no son suficientes para obtener una fila única, por lo que tiene que haber más para obtener una fila única porpost_author
There are plenty of similar questions to be found on here but I don't think that any answer the question adequately.
Para eso están las recompensas.Respuestas:
Usar un
ORDER BY
en una subconsulta no es la mejor solución para este problema.La mejor solución para obtener el
max(post_date)
autor es usar una subconsulta para devolver la fecha máxima y luego unirla a su tabla tanto en lapost_author
fecha como en la fecha máxima.La solución debería ser:
Si tiene los siguientes datos de muestra:
La subconsulta devolverá la fecha máxima y el autor de:
Luego, ya que está volviendo a unir eso a la tabla, en ambos valores devolverá los detalles completos de esa publicación.
Ver SQL Fiddle con Demo .
Para ampliar mis comentarios sobre el uso de una subconsulta para devolver con precisión estos datos.
MySQL no lo obliga a
GROUP BY
todas las columnas que incluye en laSELECT
lista. Como resultado, si solo tieneGROUP BY
una columna pero devuelve 10 columnas en total, no hay garantía depost_author
que se devuelvan los otros valores de columna que pertenecen a la columna . Si la columna no está en unGROUP BY
MySQL, elige qué valor se debe devolver.El uso de la subconsulta con la función agregada garantizará que se devuelva el autor y la publicación correctos cada vez.
Como nota al margen, si bien MySQL le permite usar un
ORDER BY
en una subconsulta y le permite aplicar unaGROUP BY
a no todas las columnas de laSELECT
lista, este comportamiento no está permitido en otras bases de datos, incluido SQL Server.fuente
wp_posts
en ambas columnas para obtener la fila completa.GROUP BY
solo a una columna, no hay garantía de que los valores en las otras columnas sean consistentemente correctos. Desafortunadamente, MySQL permite que este tipo de SELECT / GROUPing suceda que otros productos no. Dos, la sintaxis de usar unORDER BY
en una subconsulta mientras está permitida en MySQL no está permitida en otros productos de bases de datos, incluido SQL Server. Debe usar una solución que le devuelva el resultado adecuado cada vez que se ejecute.INDEX(post_author, post_date)
es importante.post_id
tu consulta interna, técnicamente también deberías agruparla , lo que probablemente sesgaría tus resultados.Su solución hace uso de un extensión de la cláusula GROUP BY que permite agrupar por algunos campos (en este caso, solo
post_author
):y seleccione columnas no agregadas:
que no se enumeran en el grupo por cláusula, o que no se utilizan en una función agregada (MIN, MAX, COUNT, etc.).
Uso correcto de la extensión a la cláusula GROUP BY
Esto es útil cuando todos los valores de las columnas no agregadas son iguales para cada fila.
Por ejemplo, suponga que tiene una mesa
GardensFlowers
(name
del jardín,flower
que crece en el jardín):y desea extraer todas las flores que crecen en un jardín, donde crecen varias flores. Luego debe usar una subconsulta, por ejemplo, podría usar esto:
Si necesita extraer todas las flores que son las únicas flores en el jardinero, puede cambiar la condición de TENER a
HAVING COUNT(DISTINCT flower)=1
, pero MySql también le permite usar esto:sin subconsulta, no SQL estándar, pero más simple.
Uso incorrecto de la extensión a la cláusula GROUP BY
Pero, ¿qué sucede si SELECCIONA columnas no agregadas que no son iguales para cada fila? ¿Cuál es el valor que elige MySql para esa columna?
Parece que MySql siempre elige el PRIMER valor que encuentra.
Para asegurarse de que el primer valor que encuentra es exactamente el valor que desea, debe aplicar
GROUP BY
a una consulta ordenada, de ahí la necesidad de utilizar una subconsulta. No puedes hacerlo de otra manera.Dado el supuesto de que MySql siempre elige la primera fila que encuentra, está ordenando correctamente las filas antes de GROUP BY. Pero desafortunadamente, si lee la documentación detenidamente, notará que esta suposición no es cierta.
Al seleccionar columnas no agregadas que no son siempre iguales, MySql es libre de elegir cualquier valor, por lo que el valor resultante que realmente muestra es indeterminado .
Veo que este truco para obtener el primer valor de una columna no agregada se usa mucho, y generalmente / casi siempre funciona, lo uso a veces también (bajo mi propio riesgo). Pero como no está documentado, no puede confiar en este comportamiento.
Este enlace (¡gracias ypercube!) El truco GROUP BY ha sido optimizado y muestra una situación en la que la misma consulta devuelve resultados diferentes entre MySql y MariaDB, probablemente debido a un motor de optimización diferente.
Entonces, si este truco funciona, es solo cuestión de suerte.
La respuesta aceptada en la otra pregunta me parece incorrecta:
wp_posts.post_date
es una columna no agregada, y su valor será oficialmente indeterminado, pero probablemente será el primero que sepost_date
encuentre. Pero dado que el truco GROUP BY se aplica a una tabla desordenada, no está seguro de cuál es el primeropost_date
encuentra.Probablemente devolverá publicaciones que son las únicas publicaciones de un solo autor, pero incluso esto no siempre es seguro.
Una posible solución
Creo que esta podría ser una posible solución:
En la consulta interna, estoy devolviendo la fecha máxima de publicación para cada autor. Entonces estoy teniendo en cuenta el hecho de que el mismo autor podría tener dos publicaciones al mismo tiempo, por lo que solo obtengo la ID máxima. Y luego estoy devolviendo todas las filas que tienen esas ID máximas. Podría hacerse más rápido utilizando combinaciones en lugar de la cláusula IN.
(Si está seguro de que
ID
solo está aumentando, y siID1 > ID2
también significa esopost_date1 > post_date2
, entonces la consulta podría hacerse mucho más simple, pero no estoy seguro de si este es el caso).fuente
extension to GROUP By
es una lectura interesante, gracias por eso.Lo que vas a leer es bastante chiflado, ¡así que no lo intentes en casa!
En SQL, en general, la respuesta a su pregunta es NO , pero debido al modo relajado de
GROUP BY
(mencionado por @bluefeet ), la respuesta es SÍ en MySQL.Supongamos que tiene un índice BTREE en (post_status, post_type, post_author, post_date). ¿Cómo se ve el índice debajo del capó?
(post_status = 'publicar', post_type = 'publicar', post_author = 'usuario A', post_date = '2012-12-01') (post_status = 'publicar', post_type = 'publicar', post_author = 'usuario A', post_date = '2012-12-31') (post_status = 'publicar', post_type = 'post', post_author = 'usuario B', post_date = '2012-10-01') (post_status = 'publicar', post_type = ' post ', post_author =' usuario B ', post_date =' 2012-12-01 ')
Es decir, los datos se ordenan por todos esos campos en orden ascendente.
Cuando está haciendo un
GROUP BY
por defecto, ordena los datos por el campo de agrupación (post_author
en nuestro caso; post_status, post_type son requeridos por laWHERE
cláusula) y si hay un índice coincidente, toma los datos de cada primer registro en orden ascendente. Esa es la consulta obtendrá lo siguiente (la primera publicación para cada usuario):(post_status = 'publicar', post_type = 'publicar', post_author = 'usuario A', post_date = '2012-12-01') (post_status = 'publicar', post_type = 'publicar', post_author = 'usuario B', post_date = '2012-10-01')
Pero
GROUP BY
en MySQL le permite especificar el orden explícitamente. Y cuando solicitepost_user
en orden descendente, recorrerá nuestro índice en el orden opuesto, aún tomando el primer registro para cada grupo que en realidad es el último.Es decir
nos dará
(post_status = 'publicar', post_type = 'publicar', post_author = 'usuario B', post_date = '2012-12-01') (post_status = 'publicar', post_type = 'publicar', post_author = 'usuario A', post_date = '2012-12-31')
Ahora, cuando ordena los resultados de la agrupación por post_date, obtiene los datos que desea.
NB :
Esto no es lo que recomendaría para esta consulta en particular. En este caso, usaría una versión ligeramente modificada de lo que sugiere @bluefeet . Pero esta técnica puede ser muy útil. Echa un vistazo a mi respuesta aquí: recuperar el último registro de cada grupo
Errores : las desventajas del enfoque es que
La ventaja es el rendimiento en casos difíciles. En este caso, el rendimiento de la consulta debe ser el mismo que en la consulta de @ bluefeet, debido a la cantidad de datos involucrados en la clasificación (todos los datos se cargan en una tabla temporal y luego se ordenan; por cierto, su consulta también requiere el
(post_status, post_type, post_author, post_date)
índice) .Lo que sugeriría :
Como dije, esas consultas hacen que MySQL pierda tiempo clasificando cantidades potencialmente enormes de datos en una tabla temporal. En caso de que necesite paginación (es decir, LIMIT está involucrado), la mayoría de los datos incluso se descartan. Lo que haría es minimizar la cantidad de datos ordenados: es decir, ordenar y limitar un mínimo de datos en la subconsulta y luego volver a unirme a la tabla completa.
La misma consulta utilizando el enfoque descrito anteriormente:
Todas esas consultas con sus planes de ejecución en SQLFiddle .
fuente
Prueba este. Simplemente obtenga la lista de las últimas fechas de publicación de cada autor . Eso es
fuente
post_date IN (select max(...) ...)
. Esto es más eficiente que hacer un grupo en una selección secundaria, consulte dev.mysql.com/doc/refman/5.6/en/subquery-optimization.htmlIN ( SELECT ... )
es mucho menos eficiente que el equivalente JOIN.No. No tiene sentido ordenar los registros antes de la agrupación, ya que la agrupación va a mutar el conjunto de resultados. La forma de subconsulta es la forma preferida. Si esto va demasiado lento, tendría que cambiar el diseño de su tabla, por ejemplo, almacenando la identificación de la última publicación para cada autor en una tabla separada, o introducir una columna booleana que indique para cada autor cuál de sus publicaciones es la última uno.
fuente
Solo use la función max y la función de grupo
fuente
Solo para recapitular, la solución estándar utiliza una subconsulta no correlacionada y se ve así:
Si está utilizando una versión antigua de MySQL, o un conjunto de datos bastante pequeño, puede utilizar el siguiente método:
fuente
** Las subconsultas pueden tener un impacto negativo en el rendimiento cuando se usan con grandes conjuntos de datos **
Consulta original
Consulta modificada
porque estoy usando
max
en elselect clause
==>max(p.post_date)
es posible evitar consultas de selección secundaria y ordenar por la columna máxima después del grupo por.fuente
Primero, no use * en select, afecta su rendimiento e impide el uso del grupo por y el orden por. Prueba esta consulta:
Cuando no especifica la tabla en ORDER BY, solo el alias, ordenarán el resultado de la selección.
fuente