2011-06-07 12 views
5

- Desplácese hacia abajo para una edición que agregué -SQL Server - Seleccione los registros más recientes de un grupo de registros similares

Así que esta es mi situación. Tengo una tabla que tiene una entrada para cada vez que alguien hace un cambio en algunos datos. La razón de esto es que necesitamos poder auditar todos los cambios.

Sin embargo, solo quiero recuperar el último registro de una serie de ediciones realizadas por un usuario.

Así que digamos que hay tres usuarios, el usuario A, B y C.

usuario A hace 10 cambios (10 entradas en la tabla). usuario B hace 5 cambia usuario A hace 3 cambios más usuario C hace 2 cambios

Lo que quiero volver a decir: más reciente de los 2 registros que C creó más reciente de los 3 registros que A creado más reciente de los 5 registros que B crea más reciente de los 10 registros que un ser creado

Por un total de 4 filas que yo vuelva

Esto es lo que he intentado, pero el problema es que la RowNum no vuelve a 1 cuando el LastUpd atedBy cambia:

WITH cte AS 
(
    SELECT 
     [LastUpdatedOn] 
     ,[LastUpdatedBy] 
     ,ROW_NUMBER() OVER(PARTITION BY [LastUpdatedBy] ORDER BY [LastUpdatedOn] DESC) [RowNum] 
    FROM [HistoricalTable] 
)   
SELECT 
    [LastUpdatedOn] 
    ,[LastUpdatedBy] 
    ,RowNum 
FROM cte 
--WHERE RowNum = 1 
ORDER BY [LastUpdatedOn] DESC; 

Y aquí está la salida de recibo (** asteriscos representan las filas que quiero volver)

LastUpdatedOn LastUpdatedBy RowNum 
**2011-06-07 13:07:26.917 629 1** 
2011-06-07 12:57:53.700 629 2 
2011-06-07 12:57:44.387 629 3 
2011-06-07 12:57:34.913 629 4 
2011-06-07 12:57:25.040 629 5 
2011-06-07 12:57:19.927 629 6 
2011-06-07 12:55:17.460 629 7 
2011-06-07 12:55:12.287 629 8 
2011-06-07 12:30:34.377 629 9 
**2011-06-07 11:54:05.727 4 1** 
**2011-06-07 11:50:02.723 629 10** (If this number went back to 1, my query would have worked fine) 
2011-06-07 11:26:43.053 629 11 
2011-06-07 10:54:32.867 629 12 
2011-06-07 10:46:32.107 629 13 
2011-06-07 10:40:52.937 629 14 
**2011-06-07 10:39:50.880 3 1** 

---------------- --- EDITAR --------------------

Así que se me ocurrió una solución, pero no es muy elegante y no estoy seguro si me gusta, pero hace el truco Esto podría darte una mejor comprensión de lo que intento lograr.

DECLARE @temp AS TABLE(LastUpdatedOn datetime, LastUpdatedBy int null, RowNum int); 

DECLARE @newTable AS TABLE(LastUpdatedOn datetime, LastUpdatedBy int null); 

DECLARE @lastUserId int = 0; 

INSERT INTO @temp 
SELECT 
    [LastUpdatedOn] 
    ,[LastUpdatedBy] 
    ,ROW_NUMBER() OVER(ORDER BY [LastUpdatedOn] DESC) [RowNum] 
    FROM [HistoricalTable] 

DECLARE @totalRecords int; 
SELECT @totalRecords = COUNT(*) FROM @temp; 
DECLARE @counter int = 0; 
WHILE @counter <= @totalRecords BEGIN 
    SET @counter = @counter + 1; 

    INSERT INTO @newTable 
    SELECT LastUpdatedOn, LastUpdatedBy 
    FROM @temp 
    WHERE RowNum = @counter AND (@lastUserId != LastUpdatedBy OR (LastUpdatedBy IS NULL)); 

    SELECT @lastUserId = LastUpdatedBy FROM @temp WHERE RowNum = @counter;  
END 

SELECT * FROM @newTable; 

Y los datos que se devuelve:

LastUpdatedOn LastUpdatedBy 
2011-06-07 13:07:26.917 629 
2011-06-07 11:54:05.727 4 
2011-06-07 11:50:02.723 629 
2011-06-07 10:39:50.880 3 
+0

Esta consulta (con WHERE RowNum = 1 sin comentar) debería darle el resultado requerido ... ya que está dividiendo el conjunto de resultados por usuario y luego ordenando el conjunto de datos en función de la fecha, debe obtener el máximo registro actualizado por cada usuario cuando filtra los registros por RowNum = 1. ¿Puede verificar nuevamente? – Chandu

+0

En su ejemplo, A realiza 10 cambios y luego A realiza 3 cambios más. Su código devolverá el cambio más reciente que A haya realizado. Pero tu pregunta dice que quieres dos filas para los cambios de A. ¿Qué diferencia al primer conjunto de cambios A del segundo? – 8kb

+0

@Cybernate - Sí, filtraría los registros si lo descomentaba, pero no del todo correctamente. Si ve mi salida de datos, verá a dónde va del usuario 629 al usuario 4, luego a 629 nuevamente. Donde va a 629 nuevamente, RowNum pasa a 10 en lugar de 1, y luego no obtendré este registro que necesito. – Makotosan

Respuesta

4
;with cte as 
(
    select *, 
    row_number() over(order by LastUpdatedOn) as rn 
    from HistoricalTable 
) 
select C1.LastUpdatedOn, 
     C1.LastUpdatedBy 
from cte as C1 
    left outer join cte as C2 
    on C1.rn = C2.rn-1 
where C1.LastUpdatedBy <> coalesce(C2.LastUpdatedBy, 0) 

La creación de un número de fila para cada orden consecutivo por LastUpdatedOn y unirse a la siguiente fila y comparar si se cambia LastUpdatedBy. Tenga cuidado con esto coalesce(C2.LastUpdatedBy, 0). Es para obtener la última fila y el 0 necesita ser un valor entero no utilizado como LastUpdatedBy.

+0

¡Sí! ¡Eso parece hacer el truco! Me dio los datos adecuados. Marcado como respuesta. – Makotosan

+1

No puedo creer que haya pasado por alto una solución tan simple, simplemente comparando el registro anterior con su cláusula de unión. ¡Gracias! – Makotosan

0

Esto es totalmente no probado, pero podría ser la base de una solución de trabajo:

SELECT 
    [Outer].[LastUpdatedOn], 
    [Outer].[LastUpdatedBy] 
FROM [HistoricalTable] AS [Outer] 
WHERE NOT EXISTS 
(
    SELECT * 
    FROM [HistoricalTable] AS [Middle] 
    WHERE [Middle].[LastUpdatedBy] = [Outer].[LastUpdatedBy] 
     AND [Middle].[LastUpdatedOn] > [Outer].[LastUpdatedOn] 
     AND [Middle].[LastUpdatedOn] <= ISNULL(
     (
      SELECT 
       MIN([Inner].[LastUpdatedOn]) 
      FROM [HistoricalTable] AS [Inner] 
      WHERE [Inner].[LastUpdatedBy] != [Outer].[LastUpdatedBy] 
       AND [Inner].[LastUpdatedOn] > [Outer].[LastUpdatedOn] 
     ), [Middle].[LastUpdatedOn]) 
) 

Incluso si este enfoque funciona, la el rendimiento probablemente sea terrible, suponiendo que tenga más que un puñado de filas.

Para cada fila en la tabla se asegura de que no exista ninguna otra fila del mismo usuario entre la fila de contexto y la fila más antigua que sea más reciente que la fila de contexto vinculada a un usuario diferente.

2

No estoy seguro de si me falta algo en su pregunta, pero el siguiente SQL no responde el problema?

declare @HistoricalTable table (LastUpdatedOn datetime, LastUpdatedBy int); 

insert into @HistoricalTable (LastUpdatedOn, LastUpdatedBy) values 
('2011-06-07 13:07:26.917', 629),('2011-06-07 12:57:53.700', 629), 
('2011-06-07 12:57:44.387', 629),('2011-06-07 12:57:34.913', 629), 
('2011-06-07 12:57:25.040', 629),('2011-06-07 12:57:19.927', 629), 
('2011-06-07 12:55:17.460', 629),('2011-06-07 12:55:12.287', 629), 
('2011-06-07 12:30:34.377', 629),('2011-06-07 11:54:05.727', 4), 
('2011-06-07 11:50:02.723', 629),('2011-06-07 11:26:43.053', 629), 
('2011-06-07 10:54:32.867', 629),('2011-06-07 10:46:32.107', 629), 
('2011-06-07 10:40:52.937', 629),('2011-06-07 10:39:50.880', 3); 

select 
latest.* 
from 
(
select *, rank() over (partition by LastUpdatedBy order by LastUpdatedOn desc) as UpdateRank 
    from @HistoricalTable 
) latest 
where 
latest.UpdateRank = 1 
order by 
latest.LastUpdatedBy; 

LastUpdatedOn   LastUpdatedBy UpdateRank 
2011-06-07 10:39:50.880   3   1 
2011-06-07 11:54:05.727   4   1 
2011-06-07 13:07:26.917   629   1 
+0

No del todo, dado que las ediciones del usuario 629 fueron interrumpidas por la edición del usuario 4, debería tener 2 resultados con LastUpdatedBy = 629 – Makotosan

+0

- debe haber omitido algo en su pregunta o datos de prueba o algo así: P pero creo que fue el " Sin embargo, solo quiero recuperar el último registro de una serie de ediciones realizadas por el usuario ". que me arrojó –

1

Me ha llamado esta mañana que este es un problema de islas. Aquí está mi solución:

CREATE TABLE #tmp (
LastUpdatedBy INT, 
LastUpdatedOn DATETIME 
) 

INSERT INTO #tmp 
     (LastUpdatedOn, LastUpdatedBy) 
VALUES ('2011-06-07 13:07:26.917', 629), 
     ('2011-06-07 12:57:53.700', 629), 
     ('2011-06-07 12:57:44.387', 629), 
     ('2011-06-07 12:57:34.913', 629), 
     ('2011-06-07 12:57:25.040', 629), 
     ('2011-06-07 12:57:19.927', 629), 
     ('2011-06-07 12:55:17.460', 629), 
     ('2011-06-07 12:55:12.287', 629), 
     ('2011-06-07 12:30:34.377', 629), 
     ('2011-06-07 11:54:05.727', 4), 
     ('2011-06-07 11:50:02.723', 629), 
     ('2011-06-07 11:26:43.053', 629), 
     ('2011-06-07 10:54:32.867', 629), 
     ('2011-06-07 10:46:32.107', 629), 
     ('2011-06-07 10:40:52.937', 629), 
     ('2011-06-07 10:39:50.880', 3) ; 

WITH cte 
      AS (SELECT [LastUpdatedOn], 
         [LastUpdatedBy], 
         ROW_NUMBER() OVER (PARTITION BY [LastUpdatedBy] ORDER BY [LastUpdatedOn] DESC) - ROW_NUMBER() OVER (ORDER BY [LastUpdatedOn] DESC) AS [Island] 
       FROM  #tmp 
      ), 
     cte2 
      AS (SELECT *, 
         ROW_NUMBER() OVER (PARTITION BY [Island] ORDER BY [LastUpdatedOn] DESC) AS [rn] 
       FROM  cte 
      ) 
    SELECT [LastUpdatedOn], 
      [LastUpdatedBy] 
    FROM cte2 
    WHERE [rn] = 1 
    ORDER BY [LastUpdatedOn] DESC ; 

El "truco" aquí es tener en cuenta que si se mantiene un registro de la row_number tanto dentro de una partición y para todo el conjunto, la diferencia entre los dos va a cambiar cuando cambia la partición.

+0

¡Buena solución también! Da el resultado correcto +1 – Makotosan

Cuestiones relacionadas