2012-02-24 13 views
12

El problema:
Tengo una tabla que registra filas de datos en foo. Cada vez que se actualiza la fila, se inserta una nueva fila junto con un número de revisión. La tabla se ve así:Seleccionar la versión más reciente y específica en cada grupo de registros, para múltiples grupos

id rev field 
1 1 test1 
2 1 fsdfs 
3 1 jfds 
1 2 test2 

Tenga en cuenta que en la tabla, el último registro es una versión más reciente de la primera fila.

¿Alguien sabe de una manera eficiente de consultar la última versión de las filas, y una versión específica de registros? Por ejemplo, una consulta para rev=2 devolvería la 2, 3 y 4ª fila (no la 1ª fila reemplazada) mientras que una consulta para rev=1 produce esas filas con rev < = 1 y en el caso de identificadores duplicados, el que tiene la revisión más alta número es elegido (registro: 1, 2, 3).

realidad no estoy seguro de si esto es posible en SQL Server ...

no preferiría devolver el resultado de forma iterativa.

+0

¿Qué versión de SQL Server está utilizando? –

+0

MS SQL 2008 R2, pero idealmente me gustaría generar una consulta que no dependa de nada específico del servidor sql. – orange

+0

¿Por qué esta pregunta está etiquetada como 'recursion' y 'rcs'? –

Respuesta

28

Para obtener únicamente últimas revisiones:

SELECT * from t t1 
WHERE t1.rev = 
    (SELECT max(rev) FROM t t2 WHERE t2.id = t1.id) 

Para obtener una revisión específica, en este caso 1 (y si un artículo no tiene la revisión sin embargo, la próxima revisión más pequeño):

SELECT * from foo t1 
WHERE t1.rev = 
    (SELECT max(rev) 
    FROM foo t2 
    WHERE t2.id = t1.id 
    AND t2.rev <= 1) 

Puede que no sea la forma más eficiente de hacerlo, pero en este momento no puedo encontrar una forma mejor de hacerlo.

+0

Gracias por la respuesta. Esto funciona bien ¿Por qué crees que es ineficiente? – orange

+1

Debido a la consulta anidada. Básicamente para cada fila en t tienes que hacer la segunda consulta. – Tim

+2

@Tim - No es cierto. SQL es declarativo no imprescindible. En este caso, SQL Server conoce este patrón y el plan es sorprendentemente simple. [plan image] (http://i.stack.imgur.com/MUb0m.jpg) –

4

Si desea que todas las últimas revisiones de cada campo, puede utilizar

SELECT C.rev, C.fields FROM (
    SELECT MAX(A.rev) AS rev, A.id 
    FROM yourtable A 
    GROUP BY A.id) 
AS B 
INNER JOIN yourtable C 
ON B.id = C.id AND B.rev = C.rev 

En el caso de tu ejemplo, que devolvería

rev field 
1 fsdfs 
1 jfds 
2 test2 
+0

No se puede encontrar la columna "A" o la función o agregado definido por el usuario "A.MAX", o el nombre es ambiguo. Este mensaje de error aparece, pero tampoco me sirve de mucho ... – orange

+0

no necesita la A. antes de MAX, solo use max como este: MAX (A.rev) o max (rev) – Tim

+0

Vaya , ¡mi error! Corregí la consulta – Treb

1
SELECT foo.* from foo 
left join foo as later 
on foo.id=later.id and later.rev>foo.rev 
where later.id is null; 
4

Ésta es la forma en que lo haría hazlo. ROW_NUMBER() requiere SQL Server 2005 o posterior

datos de la muestra:

DECLARE @foo TABLE (
    id int, 
    rev int, 
    field nvarchar(10) 
) 

INSERT @foo VALUES 
    (1, 1, 'test1'), 
    (2, 1, 'fdsfs'), 
    (3, 1, 'jfds'), 
    (1, 2, 'test2') 

La consulta:

DECLARE @desiredRev int 

SET @desiredRev = 2 

SELECT * FROM (
SELECT 
    id, 
    rev, 
    field, 
    ROW_NUMBER() OVER (PARTITION BY id ORDER BY rev DESC) rn 
FROM @foo WHERE rev <= @desiredRev 
) numbered 
WHERE rn = 1 

Los interiores SELECT devuelve todos los registros pertinentes, y dentro de cada grupo id (que es el PARTITION BY), calcula el número de fila cuando se ordena al descender rev.

El exterior SELECT simplemente selecciona el primer miembro (por lo tanto, el que tiene el más alto rev) de cada grupo id.

Salida cuando @desiredRev = 2:

id   rev   field  rn 
----------- ----------- ---------- -------------------- 
1   2   test2  1 
2   1   fdsfs  1 
3   1   jfds  1 

Salida cuando @desiredRev = 1:

id   rev   field  rn 
----------- ----------- ---------- -------------------- 
1   1   test1  1 
2   1   fdsfs  1 
3   1   jfds  1 
+0

Parece que funciona bien. Me gusta que puedas seleccionar el número de revisión tan fácilmente. Sin embargo, preferiría una consulta SQL simple si es posible. – orange

2
SELECT 
    MaxRevs.id, 
    revision.field 
FROM 
    (SELECT 
    id, 
    MAX(rev) AS MaxRev 
    FROM revision 
    GROUP BY id 
) MaxRevs 
    INNER JOIN revision 
    ON MaxRevs.id = revision.id AND MaxRevs.MaxRev = revision.rev 
+0

también Para seleccionar una revisión específica:. revisión SELECT * DE (SELECT Identificación, MAX (rev) AS MaxRev partir de la revisión DONDE rev <= 2 GRUPO POR ID ) MaxRevs INNER JOIN revisión ON MaxRevs.id = revision.id Y MaxRevs.MaxRev = revision.rev – Mahe

Cuestiones relacionadas