Chcete tedy získat řádek s nejvyšší hodnotou OrderField
za skupinu? Udělal bych to takto:
SELECT t1.*
FROM `Table` AS t1
LEFT OUTER JOIN `Table` AS t2
ON t1.GroupId = t2.GroupId AND t1.OrderField < t2.OrderField
WHERE t2.GroupId IS NULL
ORDER BY t1.OrderField; // not needed! (note by Tomas)
(EDIT od Tomáše: Pokud je ve stejné skupině více záznamů se stejným OrderField a potřebujete přesně jeden z nich, možná budete chtít podmínku rozšířit:
SELECT t1.*
FROM `Table` AS t1
LEFT OUTER JOIN `Table` AS t2
ON t1.GroupId = t2.GroupId
AND (t1.OrderField < t2.OrderField
OR (t1.OrderField = t2.OrderField AND t1.Id < t2.Id))
WHERE t2.GroupId IS NULL
konec úprav.)
Jinými slovy, vraťte řádek t1
pro který žádný jiný řádek t2
existuje se stejným GroupId
a větší OrderField
. Když t2.*
je NULL, znamená to, že levé vnější spojení nenalezlo žádnou takovou shodu, a proto t1
má největší hodnotu OrderField
ve skupině.
Žádné hodnosti, žádné poddotazy. To by mělo běžet rychle a optimalizovat přístup k t2 pomocí "Using index", pokud máte složený index na (GroupId, OrderField)
.
Pokud jde o výkon, viz moje odpověď na Načítání posledního záznamu v každé skupině . Vyzkoušel jsem metodu poddotazu a metodu spojení pomocí výpisu dat Stack Overflow. Rozdíl je pozoruhodný:metoda spojení běžela v mém testu 278krát rychleji.
Je důležité, abyste měli správný index, abyste dosáhli nejlepších výsledků!
Pokud jde o vaši metodu využívající proměnnou @Rank, nebude fungovat tak, jak jste ji napsali, protože hodnoty @Rank se po zpracování první tabulky nevynulují. Ukážu vám příklad.
Vložil jsem nějaká fiktivní data s dalším polem, které je null kromě řádku, o kterém víme, že je největší na skupinu:
select * from `Table`;
+---------+------------+------+
| GroupId | OrderField | foo |
+---------+------------+------+
| 10 | 10 | NULL |
| 10 | 20 | NULL |
| 10 | 30 | foo |
| 20 | 40 | NULL |
| 20 | 50 | NULL |
| 20 | 60 | foo |
+---------+------------+------+
Můžeme ukázat, že pořadí se zvýší na tři pro první skupinu a šest pro druhou skupinu a vnitřní dotaz je vrátí správně:
select GroupId, max(Rank) AS MaxRank
from (
select GroupId, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField) as t
group by GroupId
+---------+---------+
| GroupId | MaxRank |
+---------+---------+
| 10 | 3 |
| 20 | 6 |
+---------+---------+
Nyní spusťte dotaz bez podmínky spojení, abyste vynutili kartézský součin všech řádků, a také načteme všechny sloupce:
select s.*, t.*
from (select GroupId, max(Rank) AS MaxRank
from (select GroupId, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField
) as t
group by GroupId) as t
join (
select *, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField
) as s
-- on t.GroupId = s.GroupId and t.MaxRank = s.Rank
order by OrderField;
+---------+---------+---------+------------+------+------+
| GroupId | MaxRank | GroupId | OrderField | foo | Rank |
+---------+---------+---------+------------+------+------+
| 10 | 3 | 10 | 10 | NULL | 7 |
| 20 | 6 | 10 | 10 | NULL | 7 |
| 10 | 3 | 10 | 20 | NULL | 8 |
| 20 | 6 | 10 | 20 | NULL | 8 |
| 20 | 6 | 10 | 30 | foo | 9 |
| 10 | 3 | 10 | 30 | foo | 9 |
| 10 | 3 | 20 | 40 | NULL | 10 |
| 20 | 6 | 20 | 40 | NULL | 10 |
| 10 | 3 | 20 | 50 | NULL | 11 |
| 20 | 6 | 20 | 50 | NULL | 11 |
| 20 | 6 | 20 | 60 | foo | 12 |
| 10 | 3 | 20 | 60 | foo | 12 |
+---------+---------+---------+------------+------+------+
Z výše uvedeného můžeme vidět, že maximální hodnocení na skupinu je správné, ale @Rank se dále zvyšuje, jak zpracovává druhou odvozenou tabulku, na 7 a dále. Takže pořadí z druhé odvozené tabulky se nikdy nebude překrývat s pořadím z první odvozené tabulky.
Museli byste přidat další odvozenou tabulku, abyste vynutili @Rank resetovat se na nulu mezi zpracováním dvou tabulek (a doufat, že optimalizátor nezmění pořadí, ve kterém tabulky vyhodnocuje, jinak tomu zabráníte pomocí STRAIGHT_JOIN):
select s.*
from (select GroupId, max(Rank) AS MaxRank
from (select GroupId, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField
) as t
group by GroupId) as t
join (select @Rank := 0) r -- RESET @Rank TO ZERO HERE
join (
select *, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField
) as s
on t.GroupId = s.GroupId and t.MaxRank = s.Rank
order by OrderField;
+---------+------------+------+------+
| GroupId | OrderField | foo | Rank |
+---------+------------+------+------+
| 10 | 30 | foo | 3 |
| 20 | 60 | foo | 6 |
+---------+------------+------+------+
Ale optimalizace tohoto dotazu je hrozná. Nemůže používat žádné indexy, vytváří dvě dočasné tabulky, pracně je třídí a dokonce používá vyrovnávací paměť pro spojení, protože ani při spojování dočasných tabulek nemůže použít index. Toto je příklad výstupu z EXPLAIN
:
+----+-------------+------------+--------+---------------+------+---------+------+------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+---------------+------+---------+------+------+---------------------------------+
| 1 | PRIMARY | <derived4> | system | NULL | NULL | NULL | NULL | 1 | Using temporary; Using filesort |
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 2 | |
| 1 | PRIMARY | <derived5> | ALL | NULL | NULL | NULL | NULL | 6 | Using where; Using join buffer |
| 5 | DERIVED | Table | ALL | NULL | NULL | NULL | NULL | 6 | Using filesort |
| 4 | DERIVED | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
| 2 | DERIVED | <derived3> | ALL | NULL | NULL | NULL | NULL | 6 | Using temporary; Using filesort |
| 3 | DERIVED | Table | ALL | NULL | NULL | NULL | NULL | 6 | Using filesort |
+----+-------------+------------+--------+---------------+------+---------+------+------+---------------------------------+
Zatímco moje řešení pomocí levého vnějšího spojení se optimalizuje mnohem lépe. Nepoužívá žádnou dočasnou tabulku a dokonce hlásí "Using index"
což znamená, že dokáže vyřešit spojení pouze pomocí indexu, aniž by se dotklo dat.
+----+-------------+-------+------+---------------+---------+---------+-----------------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+---------+---------+-----------------+------+--------------------------+
| 1 | SIMPLE | t1 | ALL | NULL | NULL | NULL | NULL | 6 | Using filesort |
| 1 | SIMPLE | t2 | ref | GroupId | GroupId | 5 | test.t1.GroupId | 1 | Using where; Using index |
+----+-------------+-------+------+---------------+---------+---------+-----------------+------+--------------------------+
Pravděpodobně se dočtete, jak lidé na svých blozích tvrdí, že „připojení zpomaluje SQL“, ale to je nesmysl. Špatná optimalizace zpomaluje SQL.