Consigue la primera fila de cada grupo
Tengo una tabla en la que quiero obtener la última entrada para cada grupo. Aquí está la tabla:
DocumentStatusLogs
Mesa
IDENTIFICACIÓN | ID del documento | Estado | Fecha de creacion |
---|---|---|---|
2 | 1 | T1 | 29/07/2011 |
3 | 1 | T2 | 30/07/2011 |
6 | 1 | T1 | 8/02/2011 |
1 | 2 | T1 | 28/07/2011 |
4 | 2 | T2 | 30/07/2011 |
5 | 2 | T3 | 01/08/2011 |
6 | 3 | T1 | 8/02/2011 |
La tabla se agrupará DocumentID
y ordenará DateCreated
en orden descendente. Para cada uno DocumentID
, quiero obtener el estado más reciente.
Mi salida preferida:
ID del documento | Estado | Fecha de creacion |
---|---|---|
1 | T1 | 8/02/2011 |
2 | T3 | 01/08/2011 |
3 | T1 | 8/02/2011 |
¿Existe alguna función agregada para obtener solo los mejores de cada grupo? Vea el pseudocódigo
GetOnlyTheTop
a continuación:SELECT DocumentID, GetOnlyTheTop(Status), GetOnlyTheTop(DateCreated) FROM DocumentStatusLogs GROUP BY DocumentID ORDER BY DateCreated DESC
Si dicha función no existe, ¿hay alguna manera de lograr el resultado que deseo?
O, en primer lugar, ¿podría esto deberse a una base de datos no normalizada? Estoy pensando, dado que lo que estoy buscando es solo una fila, ¿debería
status
estar ubicada también en la tabla principal?
Consulte la tabla principal para obtener más información:
Documents
Tabla actual
ID del documento | Título | Contenido | Fecha de creacion |
---|---|---|---|
1 | TítuloA | ... | ... |
2 | TítuloB | ... | ... |
3 | TítuloC | ... | ... |
¿La tabla principal debería ser así para poder acceder fácilmente a su estado?
ID del documento | Título | Contenido | Fecha de creacion | Estado actual |
---|---|---|---|---|
1 | TítuloA | ... | ... | s1 |
2 | TítuloB | ... | ... | s3 |
3 | TítuloC | ... | ... | s1 |
ACTUALIZACIÓN Acabo de aprender a utilizar "aplicar", lo que facilita la solución de estos problemas.
;WITH cte AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY DocumentID ORDER BY DateCreated DESC) AS rn
FROM DocumentStatusLogs
)
SELECT *
FROM cte
WHERE rn = 1
Si espera 2 entradas por día, entonces elegirá una arbitrariamente. Para obtener ambas entradas para un día, use DENSE_RANK en su lugar
En cuanto a normalizado o no, depende si quieres:
- mantener el estado en 2 lugares
- preservar el historial de estado
- ...
Tal como está, conserva el historial de estado. Si también desea el estado más reciente en la tabla principal (que es desnormalización), necesitará un disparador para mantener el "estado" en la tabla principal. o elimine esta tabla de historial de estado.
Acabo de aprender a usarlo cross apply
. A continuación se explica cómo usarlo en este escenario:
select d.DocumentID, ds.Status, ds.DateCreated
from Documents as d
cross apply
(select top 1 Status, DateCreated
from DocumentStatusLogs
where DocumentID = d.DocumentId
order by DateCreated desc) as ds
Sé que este es un hilo antiguo, pero las TOP 1 WITH TIES
soluciones son bastante buenas y pueden ser útiles para leerlas.
select top 1 with ties
DocumentID
,Status
,DateCreated
from DocumentStatusLogs
order by row_number() over (partition by DocumentID order by DateCreated desc)
La select top 1 with ties
cláusula le dice a SQL Server que desea devolver la primera fila por grupo. Pero, ¿cómo sabe SQL Server cómo agrupar los datos? Aquí es donde order by row_number() over (partition by DocumentID order by DateCreated desc
entra en juego. La columna/columnas posteriores partition by
definen cómo SQL Server agrupa los datos. Dentro de cada grupo, las filas se ordenarán según las order by
columnas. Una vez ordenado, la fila superior de cada grupo se devolverá en la consulta.
Puede encontrar más información sobre la cláusula TOP aquí .