2012-02-20 14 views
5

widziałem sporo rozwiązań na tego rodzaju problem, ale żaden z nich nie wydaje się być odpowiedni:Wybierz wiersze z MAX wartość pogrupowane według dwóch kolumn

mam następującą tabelę układ, o wersjonowanie załączników, które są zobowiązane do podmiotów:

TABLE attachments 
+------+--------------+----------+----------------+---------------+ 
| id | entitiy_id | group_id | version_number | filename  | 
+------+--------------+----------+----------------+---------------+ 
| 1 | 1   | 1  | 1    | file1-1.pdf | 
| 2 | 1   | 1  | 2    | file1-2.pdf | 
| 3 | 1   | 2  | 1    | file2-1.pdf | 
| 4 | 2   | 1  | 1    | file1-1.pdf | 
| 5 | 2   | 1  | 2    | file1-2.pdf | 
| 6 | 2   | 3  | 1    | file3-1.pdf | 
+------+--------------+----------+----------------+---------------+ 

wyjścia powinna być maksymalna liczba wersji, pogrupowane według group_id i ENTITY_ID, że muszę tylko listę dla pojedynczych entity_ids czy to pomaga:

+------+--------------+----------+----------------+---------------+ 
| id | entitiy_id | group_id | version_number | filename  | 
+------+--------------+----------+----------------+---------------+ 
| 2 | 1   | 1  | 2    | file1-2.pdf | 
| 3 | 1   | 2  | 1    | file2-1.pdf | 
| 5 | 2   | 1  | 2    | file1-2.pdf | 
| 6 | 2   | 3  | 1    | file3-1.pdf | 
+------+--------------+----------+----------------+---------------+ 

Co mam wymyślić jest to samo dołączyć:

SELECT * 
FROM `attachments` `attachments` 
     LEFT OUTER JOIN attachments t2 
     ON (attachments.group_id = t2.group_id 
       AND attachments.version_number < t2.version_number) 
WHERE (t2.group_id IS NULL) 
    AND (`t2`.`id` = 1) 
GROUP BY t2.group_id 

Ale to działa tylko wtedy, gdy różne podmioty nie podzielają te same numery grup. Jest to niestety konieczne.

Podczas tworzenia widoku natknąłem się na działające rozwiązanie, ale nie jest to obsługiwane w mojej bieżącej konfiguracji.

Wszelkie pomysły są wysoko cenione. Dzięki!

Odpowiedz

3

Spróbuj tego:

select t1.* from attachments t1 
left join attachments t2 
on t1.entity_id = t2.entity_id and t1.group_id = t2.group_id and 
    t1.version_number < t2.version_number 
where t2.version_number is null 
+0

Dziękujemy! Jest to bardzo proste rozwiązanie. –

+0

Zapewniam przynajmniej, że dodasz klucz, który zasugerowałem, aby utrzymać wydajność tabeli, więc entitiy_id, group_id i numer_wersji. W swoim pytaniu określiłeś, że jest to identyfikator pojedynczego obiektu, a ta opcja będzie działała wolniej niż inne sugestie przy wyższych woluminach tabel, ponieważ identyfikator entity_id nie jest używany do ograniczania łączenia. –

+0

-1 dla Non-ANSI –

2

to będzie działać do wyboru wszystkie

SELECT attachments.* 
FROM (
    SELECT entitiy_id, group_id, MAX(version_number) AS max_version 
    FROM attachments 
    GROUP BY entitiy_id, group_id 
) AS maxVersions 
INNER JOIN attachments 
ON attachments.entitiy_id = maxVersions.entitiy_id 
AND attachments.group_id = maxVersions.group_id 
AND attachments.version_number = maxVersions.max_version 

Rozszerzenie to wystarczy spojrzeć na jednym entitiy_id po prostu polegać na dodawaniu WHERE do podzapytania, więc dałoby

SELECT attachments.* 
FROM (
    SELECT entitiy_id, group_id, MAX(version_number) AS max_version 
    FROM attachments 
    WHERE entitity_id = [[YOUR ENTITIY ID HERE]] 
    GROUP BY entitiy_id, group_id 
) AS maxVersions 
INNER JOIN attachments 
ON attachments.entitiy_id = maxVersions.entitiy_id 
AND attachments.group_id = maxVersions.group_id 
AND attachments.version_number = maxVersions.max_version 

Jeśli chcesz aby upewnić się, że to działa nadal szybko wraz ze wzrostem liczby wierszy, radziłbym upewnić się, że dodajesz klucz do załączników z wierszami (entitiy_id, group_id, max_version), ponieważ wówczas podzapytanie będzie w stanie na nich polegać, zapewniając tym samym, że nie zostanie zablokowany do góry na stół.

2

będzie to rade:

select a1.* from attachments a1 
inner join (select entitiy_id, group_id, max(version_number) as version_number 
      from attachments 
      group by entitiy_id, group_id) a2 on a1.entitiy_id = a2.entitiy_id and 
                a1.group_id = a2.group_id and 
                a1.version_number = a2.version_number 
0

Można również rozwiązać ten problem przy użyciu wysokiej wydajności wspólnym stole wyrażenie (CTE).

WITH CTE AS 
(
SELECT entitiy_id, group_id, version_number, filename,  
ROW_NUMBER() OVER (PARTITION BY entitiy_id, group_id ORDER BY version_number DESC) as RowNum 
FROM attachments 
) 
SELECT entitiy_id, group_id, version_number, filename 
FROM CTE 
WHERE RowNum = 1 

Albo

SELECT T.entitiy_id, T.group_id, T.version_number, T.filename 
FROM (SELECT entitiy_id, group_id, version_number, filename,  
    ROW_NUMBER() OVER (PARTITION BY entitiy_id, group_id ORDER BY version_number DESC) as RowNum 
    FROM attachments 
    ) as T 
WHERE RowNum = 1 
Powiązane problemy