Yani OrderField
grup başına en yüksek olan sırayı mı almak istiyorsunuz ? Bunu şu şekilde yapardım:
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)
( Tomas DÜZENLEME: Aynı grup içinde aynı OrderField ile daha fazla kayıt varsa ve bunlardan tam olarak birine ihtiyacınız varsa, koşulu genişletmek isteyebilirsiniz:
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
düzenlemenin sonu.)
Başka bir deyişle, aynı ve daha büyük t1
olan başka hiçbir satırı olmayan satırı t2
döndür . NULL olduğunda , sol dış birleşim böyle bir eşleşme bulamadı ve bu nedenle grup içinde en büyük değere sahip olduğu anlamına gelir .GroupId
OrderField
t2.*
t1
OrderField
Derece yok, alt sorgu yok. Bu, hızlı çalışmalı ve üzerinde bir bileşik indeksiniz varsa "İndeksi kullanarak" t2'ye erişimi optimize etmelidir (GroupId, OrderField)
.
Performansla ilgili olarak, her gruptaki son kaydı alma konusundaki cevabıma bakın . Stack Overflow veri dökümünü kullanarak bir alt sorgu yöntemi ve birleştirme yöntemini denedim. Aradaki fark dikkat çekicidir: Birleştirme yöntemi benim testimde 278 kat daha hızlı çalıştı.
En iyi sonuçları almak için doğru dizine sahip olmanız önemlidir!
@Rank değişkenini kullanan yönteminizle ilgili olarak, yazdığınız şekilde çalışmayacaktır, çünkü sorgu ilk tabloyu işledikten sonra @Rank değerleri sıfırlanmayacaktır. Sana bir örnek göstereceğim.
Grup başına en büyük olduğunu bildiğimiz satır dışında boş olan fazladan bir alanla bazı sahte veriler ekledim:
select * from `Table`;
+
| GroupId | OrderField | foo |
+
| 10 | 10 | NULL |
| 10 | 20 | NULL |
| 10 | 30 | foo |
| 20 | 40 | NULL |
| 20 | 50 | NULL |
| 20 | 60 | foo |
+
Sıralamanın birinci grup için üçe, ikinci grup için altıya yükseldiğini ve iç sorgunun bunları doğru şekilde döndürdüğünü gösterebiliriz:
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 |
+
Şimdi tüm satırların Kartezyen çarpımını zorlamak için birleştirme koşulu olmadan sorguyu çalıştırın ve ayrıca tüm sütunları getirelim:
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
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 |
+
Yukarıdan, grup başına maksimum sıranın doğru olduğunu görebiliriz, ancak daha sonra, ikinci türetilmiş tabloyu 7'ye ve daha yükseğe işlerken @Rank artmaya devam eder. Dolayısıyla, ikinci türetilmiş tablodaki sıralar, ilk türetilmiş tablodaki sıralarla hiçbir zaman çakışmayacaktır.
İki tabloyu işlerken @Rank'i sıfırlamaya zorlamak için başka bir türetilmiş tablo eklemeniz gerekir (ve optimize edicinin tabloları değerlendirdiği sırayı değiştirmemesini veya bunu önlemek için STRAIGHT_JOIN kullanmasını umarsınız):
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
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 |
+
Ancak bu sorgunun optimizasyonu korkunç. Herhangi bir dizin kullanamaz, iki geçici tablo oluşturur, bunları zor bir şekilde sıralar ve hatta bir birleştirme tamponu kullanır çünkü geçici tabloları birleştirirken bir dizin de kullanamaz. Bu, aşağıdakilerden örnek çıktıdır 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 |
+
Sol dış birleştirmeyi kullanan çözümüm çok daha iyi optimize ediyor. Hiçbir geçici tablo ve hatta raporlar "Using index"
kullanmaz, bu da verilere dokunmadan yalnızca indeksi kullanarak birleştirmeyi çözebileceği anlamına gelir.
+
| 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 |
+
Muhtemelen bloglarında "birleştirme SQL'i yavaşlatan" iddialarda bulunan insanları okuyacaksınız, ama bu çok saçma. Yetersiz optimizasyon, SQL'i yavaşlatır.