Så du vill få raden med det högsta OrderField
per grupp? Jag skulle göra så här:
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 av Tomas: Om det finns fler poster med samma OrderField inom samma grupp och du behöver exakt en av dem, kanske du vill utöka villkoret:
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
slutet av redigeringen.)
Med andra ord, returnera raden t1
för vilken ingen annan rad t2
finns med samma GroupId
och ett större OrderField
. När t2.*
är NULL betyder det att den vänstra yttre kopplingen inte hittade någon sådan matchning, och därför t1
har det största värdet av OrderField
i gruppen.
Inga rangord, inga underfrågor. Detta bör gå snabbt och optimera åtkomsten till t2 med "Using index" om du har ett sammansatt index på (GroupId, OrderField)
.
Angående prestanda, se mitt svar på Hämta den sista posten i varje grupp . Jag försökte en subquery-metod och join-metoden med Stack Overflow-datadumpen. Skillnaden är anmärkningsvärd:joinmetoden gick 278 gånger snabbare i mitt test.
Det är viktigt att du har rätt index för att få bästa resultat!
När det gäller din metod som använder @Rank-variabeln, kommer den inte att fungera som du har skrivit den, eftersom värdena för @Rank inte återställs till noll efter att frågan har bearbetat den första tabellen. Jag ska visa dig ett exempel.
Jag infogade lite dummydata, med ett extra fält som är null förutom på raden som vi vet är störst per grupp:
select * from `Table`;
+---------+------------+------+
| GroupId | OrderField | foo |
+---------+------------+------+
| 10 | 10 | NULL |
| 10 | 20 | NULL |
| 10 | 30 | foo |
| 20 | 40 | NULL |
| 20 | 50 | NULL |
| 20 | 60 | foo |
+---------+------------+------+
Vi kan visa att rangordningen ökar till tre för den första gruppen och sex för den andra gruppen, och den inre frågan returnerar dessa korrekt:
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 |
+---------+---------+
Kör nu frågan utan kopplingsvillkor, för att tvinga fram en kartesisk produkt av alla rader, och vi hämtar även alla kolumner:
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 |
+---------+---------+---------+------------+------+------+
Vi kan se från ovan att maxrankningen per grupp är korrekt, men sedan fortsätter @Rank att öka när den bearbetar den andra härledda tabellen, till 7 och högre. Så rankningarna från den andra härledda tabellen kommer aldrig att överlappa med rankningarna från den första härledda tabellen alls.
Du måste lägga till en annan härledd tabell för att tvinga @Rank att återställas till noll mellan bearbetningen av de två tabellerna (och hoppas att optimeraren inte ändrar ordningen i vilken den utvärderar tabeller, eller använd STRAIGHT_JOIN för att förhindra det):
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 |
+---------+------------+------+------+
Men optimeringen av denna fråga är fruktansvärd. Den kan inte använda några index, den skapar två temporära tabeller, sorterar dem på den hårda vägen och använder till och med en kopplingsbuffert eftersom den inte heller kan använda ett index när den går med i tillfälliga tabeller. Detta är exempel på utdata från 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 |
+----+-------------+------------+--------+---------------+------+---------+------+------+---------------------------------+
Medan min lösning med den vänstra yttre skarven optimerar mycket bättre. Den använder ingen temptabell och rapporterar till och med "Using index"
vilket innebär att det kan lösa sammanfogningen med endast indexet, utan att röra data.
+----+-------------+-------+------+---------------+---------+---------+-----------------+------+--------------------------+
| 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 |
+----+-------------+-------+------+---------------+---------+---------+-----------------+------+--------------------------+
Du kommer förmodligen att läsa folk som påstår på sina bloggar att "anslutningar gör SQL långsam", men det är nonsens. Dålig optimering gör SQL långsam.