2012-02-24 6 views
12

Problem:
Mam tabelę, która rejestruje wiersze danych w foo. Za każdym razem, gdy wiersz jest aktualizowany, wstawiany jest nowy wiersz wraz z numerem wersji. Tabela wygląda następująco:Wybieranie najnowszej i określonej wersji w każdej grupie rekordów dla wielu grup

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

Należy zauważyć, że w tabeli ostatnim rekordem jest nowsza wersja pierwszego wiersza.

Czy ktoś wie na temat skutecznego sposobu sprawdzania najnowszej wersji wierszy i konkretnej wersji rekordów? Na przykład zapytanie o numer rev=2 zwróci wiersz 2, 3 i 4 (nie wymieniony pierwszy wiersz), natomiast zapytanie o numer rev=1 powoduje, że wiersze z rev < = 1 i w przypadku duplikatów id, o wyższej wersji numer jest wybrany (rekord: 1, 2, 3).

Nie jestem pewien, czy rzeczywiście jest to w ogóle możliwe w SQL Server ...

nie wolałby, aby powrócić wynik w iteracyjny sposób.

+0

Jaka wersja serwera sql używasz? –

+0

MS SQL 2008 R2, ale najlepiej chciałbym wygenerować kwerendę, która nie zależy od niczego szczegóły serwera sql. – orange

+0

Dlaczego to pytanie jest oznaczone jako "rekursja" i "rcs"? –

Odpowiedz

28

uzyskać tylko najnowsze wersje:

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

Aby uzyskać konkretną wersję, w tym przypadku 1 (a jeśli przedmiot nie posiada rewizji jeszcze następna najmniejsza przegląd):

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

To może nie być najskuteczniejszy sposób, aby to zrobić, ale teraz nie mogę wymyślić lepszego sposobu na zrobienie tego.

+0

Dzięki za odpowiedź. To działa dobrze. Dlaczego uważasz, że jest to nieefektywne? – orange

+1

Z powodu zagnieżdżonego zapytania. Zasadniczo dla każdego rzędu wt musisz wykonać drugie zapytanie. – Tim

+2

@Tim - Nieprawda. SQL jest deklaratywny, a nie imperatywny. W tym przypadku SQL Server zna ten wzorzec, a plan jest zaskakująco prosty. [obraz planu] (http://i.stack.imgur.com/MUb0m.jpg) –

4

Jeśli chcesz wszystkie najnowsze wersje każdego pola, można użyć

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 

W przypadku swoim przykładzie, że wróci

rev field 
1 fsdfs 
1 jfds 
2 test2 
+0

Nie można znaleźć kolumny "A" lub funkcji zdefiniowanej przez użytkownika lub zagregować "A.MAX" lub nazwa jest niejednoznaczna. Ten komunikat o błędzie pojawia się, ale nie pomaga mi to zbytnio ... – orange

+0

Nie potrzebujesz A. przed MAX, po prostu użyj max jak poniżej: MAX (A.rev) lub max (rev) – Tim

+0

Oops , mój błąd! Poprawiono zapytanie. – 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

To jak bym Zrób to. ROW_NUMBER() wymaga SQL Server 2005 lub później

dane próbki:

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') 

Zapytanie:

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 

Wewnętrzne SELECT zwraca wszystkie odpowiednie zapisy, a wewnątrz każdej grupy id (to PARTITION BY) oblicza numer wiersza, gdy jest uporządkowany, opadając rev.

Zewnętrzna SELECT po prostu wybiera pierwszy element (czyli ten o najwyższej wartości rev) z każdej grupy id.

Wyjście gdy @desiredRev = 2:

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

Wyjście gdy @desiredRev = 1:

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

Wydaje się działać poprawnie. Podoba mi się, że możesz łatwo wybrać numer rewizji. Jednak wolałbym, jeśli to możliwe, zwykłe zapytanie SQL. – 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

Aby także wybrać określoną zmianę. SELECT zmiana * Z (SELECT id MAX (rev) jako MaxRev Z przeglądu GDZIE obr <2 GROUP przez ID ) MaxRevs WEWNĘTRZNY Dołącz zmiany ON MaxRevs.id = revision.id AND MaxRevs.MaxRev = revision.rev – Mahe

Powiązane problemy