Tengo una tabla en la que quiero obtener la última entrada para cada grupo. Aquí está la tabla:
DocumentStatusLogs
Mesa
|ID| DocumentID | Status | DateCreated |
| 2| 1 | S1 | 7/29/2011 |
| 3| 1 | S2 | 7/30/2011 |
| 6| 1 | S1 | 8/02/2011 |
| 1| 2 | S1 | 7/28/2011 |
| 4| 2 | S2 | 7/30/2011 |
| 5| 2 | S3 | 8/01/2011 |
| 6| 3 | S1 | 8/02/2011 |
La tabla se agrupará DocumentID
y ordenará por DateCreated
orden descendente. Para cada uno DocumentID
, quiero obtener el último estado.
Mi salida preferida:
| DocumentID | Status | DateCreated |
| 1 | S1 | 8/02/2011 |
| 2 | S3 | 8/01/2011 |
| 3 | S1 | 8/02/2011 |
¿Hay alguna función agregada para obtener solo la parte superior de cada grupo? Ver pseudocódigo a
GetOnlyTheTop
continuación:SELECT DocumentID, GetOnlyTheTop(Status), GetOnlyTheTop(DateCreated) FROM DocumentStatusLogs GROUP BY DocumentID ORDER BY DateCreated DESC
Si tal función no existe, ¿hay alguna forma de lograr el resultado que quiero?
- O, en primer lugar, ¿podría ser esto causado por una base de datos no normalizada? Estoy pensando, ya que lo que estoy buscando es solo una fila, ¿debería
status
estar también ubicado en la tabla principal?
Consulte la tabla principal para obtener más información:
Documents
Tabla actual
| DocumentID | Title | Content | DateCreated |
| 1 | TitleA | ... | ... |
| 2 | TitleB | ... | ... |
| 3 | TitleC | ... | ... |
¿La tabla principal debería ser así para poder acceder fácilmente a su estado?
| DocumentID | Title | Content | DateCreated | CurrentStatus |
| 1 | TitleA | ... | ... | s1 |
| 2 | TitleB | ... | ... | s3 |
| 3 | TitleC | ... | ... | s1 |
ACTUALIZACIÓN Acabo de aprender a usar "aplicar", lo que hace que sea más fácil abordar tales problemas.
Respuestas:
Si espera 2 entradas por día, esto arbitrariamente elegirá una. Para obtener ambas entradas por un día, use DENSE_RANK en su lugar
En cuanto a normalizado o no, depende si desea:
Tal como está, conserva el historial de estado. Si también desea el último estado en la tabla principal (que es la desnormalización) necesitaría un activador para mantener el "estado" en el principal. o descartar esta tabla de historial de estado.
fuente
Partition By
?With
también es nuevo para mí :( Estoy usando mssql 2005 de todos modos.ROW_NUMBER
algún tipo de subconsulta para cada fila?Acabo de aprender a usar
cross apply
. Aquí se explica cómo usarlo en este escenario:fuente
He hecho algunos tiempos sobre las diversas recomendaciones aquí, y los resultados realmente dependen del tamaño de la tabla involucrada, pero la solución más consistente es usar CROSS APPLY. Estas pruebas se ejecutaron en SQL Server 2008-R2, usando una tabla con 6.500 registros, y otro (esquema idéntico) con 137 millones de registros. Las columnas que se consultan son parte de la clave primaria en la tabla, y el ancho de la tabla es muy pequeño (aproximadamente 30 bytes). SQL Server informa los tiempos del plan de ejecución real.
Creo que lo realmente sorprendente fue cuán consistente fue el tiempo para la APLICACIÓN CRUZADA, independientemente de la cantidad de filas involucradas.
fuente
Sé que este es un hilo viejo pero el
TOP 1 WITH TIES
soluciones son bastante buenas y podrían ser útiles para leer algunas de las soluciones.Puede encontrar más información sobre la cláusula TOP aquí .
fuente
Si le preocupa el rendimiento, también puede hacerlo con MAX ():
ROW_NUMBER () requiere una especie de todas las filas en su instrucción SELECT, mientras que MAX no. Debería acelerar drásticamente su consulta.
fuente
row_number()
incluso con una indexación adecuada. Lo encuentro especialmente valioso en escenarios de autounión. Sin embargo, lo que hay que tener en cuenta es que este método a menudo producirá un mayor número de lecturas lógicas y recuentos de escaneo, a pesar de informar un bajo costo de subárbol. Deberá sopesar el costo / beneficios en su caso particular para determinar si en realidad es mejor.¿Qué servidor de base de datos? Este código no funciona en todos ellos.
Con respecto a la segunda mitad de su pregunta, me parece razonable incluir el estado como una columna. Puedes irte
DocumentStatusLogs
como un registro, pero aún almacenar la información más reciente en la tabla principal.Por cierto, si ya tiene la
DateCreated
columna en la tabla Documentos, puede unirseDocumentStatusLogs
usando eso (siempre queDateCreated
sea único enDocumentStatusLogs
).Editar: MsSQL no admite el USO, así que cámbielo a:
fuente
max(DateCreated)
Esta es una de las preguntas más fáciles de encontrar sobre el tema, por lo que quería dar una respuesta moderna (tanto para mi referencia como para ayudar a otros). Al usar
first_value
yover
puede hacer un breve trabajo de la consulta anterior:Esto debería funcionar en Sql Server 2008 y versiones posteriores.
First_value
puede considerarse como una forma de lograrloSelect Top 1
cuando se usa unaover
cláusulaOver
permite la agrupación en la lista de selección, por lo que en lugar de escribir subconsultas anidadas (como hacen muchas de las respuestas existentes), esto lo hace de una manera más legible. Espero que esto ayude.fuente
Este es un hilo bastante antiguo, pero pensé que arrojaría mis dos centavos de la misma manera que la respuesta aceptada no funcionó particularmente bien para mí. Probé la solución de gbn en un gran conjunto de datos y descubrí que era terriblemente lenta (> 45 segundos con más de 5 millones de registros en SQL Server 2012). Al observar el plan de ejecución, es obvio que el problema es que requiere una operación SORT que ralentiza las cosas significativamente.
Aquí hay una alternativa que saqué del marco de la entidad que no necesita operación SORT y realiza una búsqueda de índice NO agrupado. Esto reduce el tiempo de ejecución a <2 segundos en el conjunto de registros antes mencionado.
Ahora estoy asumiendo algo que no está completamente especificado en la pregunta original, pero si el diseño de su tabla es tal que su columna de ID es un ID de incremento automático, y DateCreated se establece en la fecha actual con cada inserción, entonces incluso sin ejecutar mi consulta anterior, en realidad podría obtener un aumento considerable del rendimiento de la solución de gbn (aproximadamente la mitad del tiempo de ejecución) simplemente ordenando en ID en lugar de ordenar en DateCreated, ya que esto proporcionará un orden de clasificación idéntico y es una clasificación más rápida.
fuente
Mi código para seleccionar el top 1 de cada grupo
fuente
Verificando la asombrosa y correcta respuesta de Clint desde arriba:
El rendimiento entre las dos consultas a continuación es interesante. 52% es el mejor. Y el 48% es el segundo. Una mejora del 4% en el rendimiento utilizando DISTINCT en lugar de ORDER BY. Pero ORDER BY tiene la ventaja de ordenar por múltiples columnas.
Opción 1:
Opcion 2:
Management Studio de M $: después de resaltar y ejecutar el primer bloque, resalte la opción 1 y la opción 2, haga clic con el botón derecho -> [Mostrar plan de ejecución estimado]. Luego ejecute todo para ver los resultados.
Opción 1 Resultados:
Opción 2 Resultados:
Nota:
También evito las subconsultas EXISTS / IN en la cláusula WHERE u ON, ya que he experimentado que esto causa algunos planes de ejecución terribles. Pero el kilometraje varía. ¡Revise el plan de ejecución y el rendimiento del perfil donde y cuando sea necesario!
fuente
Esta solución se puede usar para obtener las N filas más recientes TOP para cada partición (en el ejemplo, N es 1 en la instrucción WHERE y la partición es doc_id):
fuente
Si desea devolver solo el pedido de documentos recientes por DateCreated, devolverá solo el documento 1 superior por DocumentID
fuente
CROSS APPLY
fue el método que utilicé para mi solución, ya que funcionó para mí y para las necesidades de mis clientes. Y por lo que he leído, debería proporcionar el mejor rendimiento general en caso de que su base de datos crezca sustancialmente.fuente
Aquí hay 3 enfoques diferentes para el problema en cuestión junto con las mejores opciones de indexación para cada una de esas consultas (pruebe los índices y vea la lectura lógica, el tiempo transcurrido y el plan de ejecución. He proporcionado las sugerencias de mi experiencia en tales consultas sin ejecutar para este problema específico).
Enfoque 1 : Uso de ROW_NUMBER (). Si el índice de almacén de filas no puede mejorar el rendimiento, puede probar el índice de almacén de columnas no agrupado / agrupado para consultas con agregación y agrupación y para tablas que están ordenadas por columnas diferentes todo el tiempo, el índice de almacén de columnas generalmente es la mejor opción.
Enfoque 2 : Uso de FIRST_VALUE. Si el índice de almacén de filas no puede mejorar el rendimiento, puede probar el índice de almacén de columnas no agrupado / agrupado para consultas con agregación y agrupación y para tablas que están ordenadas por columnas diferentes todo el tiempo, el índice de almacén de columnas generalmente es la mejor opción.
Enfoque 3 : Uso de CROSS APPLY. Crear un índice de almacén de filas en la tabla DocumentStatusLogs que cubra las columnas utilizadas en la consulta debería ser suficiente para cubrir la consulta sin necesidad de un índice de almacén de columnas.
fuente
Creo que esto se puede hacer así. Esto puede necesitar algunos ajustes, pero puede seleccionar el máximo del grupo.
Estas respuestas son excesivas ...
fuente
En los escenarios en los que desea evitar el uso de row_count (), también puede usar una combinación izquierda:
Para el esquema de ejemplo, también podría usar un "no en subconsulta", que generalmente se compila en la misma salida que la combinación izquierda:
Tenga en cuenta que el patrón de subconsulta no funcionaría si la tabla no tuviera al menos una clave / restricción / índice único de una sola columna, en este caso la clave primaria "Id".
Ambas consultas tienden a ser más "caras" que la consulta row_count () (medida por el Analizador de consultas). Sin embargo, puede encontrar escenarios en los que devuelven resultados más rápido o permiten otras optimizaciones.
fuente
fuente
Prueba esto:
fuente
Esta es la TSQL más vainilla que se me ocurre
fuente
En SQLite se marca que puede usar la siguiente consulta simple con GROUP BY
Aquí MAX ayuda a obtener el DateCreated máximo de cada grupo.
Pero parece que MYSQL no asocia * -columns con el valor de max DateCreated :(
fuente