Vì vậy, bạn muốn nhận hàng có mức cao nhất là OrderField
cho mỗi nhóm? Tôi muốn làm điều đó theo cách này:
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 bởi Tomas: Nếu có nhiều hồ sơ với các OrderField giống nhau trong cùng một nhóm và bạn cần phải chính xác một trong số họ, bạn có thể muốn mở rộng điều kiện :
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
cuối chỉnh sửa)
Nói cách khác, trả lại hàng t1
mà không có hàng khác t2
tồn tại với cùng GroupId
và lớn hơn.. Khi t2.*
là NULL, nó có nghĩa là bên ngoài tham gia bên ngoài không tìm thấy trận đấu như vậy, và do đó t1
có giá trị lớn nhất của OrderField
trong nhóm.
Không có thứ hạng, không có truy vấn phụ. Điều này sẽ chạy nhanh và tối ưu hóa quyền truy cập vào t2 bằng "Sử dụng chỉ mục" nếu bạn có chỉ mục phức hợp trên (GroupId, OrderField)
.
Về hiệu suất, hãy xem câu trả lời của tôi cho Retrieving the last record in each group. Tôi đã thử một phương thức truy vấn con và phương thức kết nối bằng cách sử dụng kết xuất dữ liệu Stack Overflow. Sự khác biệt là đáng chú ý: phương pháp kết nối chạy nhanh hơn 278 lần trong thử nghiệm của tôi.
Điều quan trọng là bạn có đúng chỉ mục để có kết quả tốt nhất!
Về phương pháp của bạn bằng biến @Rank, nó sẽ không hoạt động như bạn đã viết, bởi vì các giá trị của @Rank sẽ không được đặt lại về 0 sau khi truy vấn đã xử lý bảng đầu tiên. Tôi sẽ cho bạn xem một ví dụ.
tôi chèn một số dữ liệu giả, với trường bổ sung mà là null trừ trên hàng chúng ta biết là lớn nhất cho mỗi nhóm:
select * from `Table`;
+---------+------------+------+
| GroupId | OrderField | foo |
+---------+------------+------+
| 10 | 10 | NULL |
| 10 | 20 | NULL |
| 10 | 30 | foo |
| 20 | 40 | NULL |
| 20 | 50 | NULL |
| 20 | 60 | foo |
+---------+------------+------+
Chúng ta có thể thấy rằng cấp bậc tăng lên ba cho nhóm đầu tiên và sáu cho nhóm thứ hai, và truy vấn nội trả về những cách chính xác:
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 |
+---------+---------+
Bây giờ chạy các truy vấn không có điều kiện join, để buộc một sản phẩm Descartes của tất cả các hàng, và chúng tôi cũng lấy tất cả các cột:
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 |
+---------+---------+---------+------------+------+------+
Chúng ta có thể thấy từ trên rằng xếp hạng tối đa cho mỗi nhóm là chính xác, nhưng sau đó @Rank tiếp tục tăng khi nó xử lý bảng dẫn xuất thứ hai, lên 7 và cao hơn. Vì vậy, các cấp bậc từ bảng dẫn xuất thứ hai sẽ không bao giờ chồng lên nhau với các cấp bậc từ bảng dẫn xuất đầu tiên.
Bạn sẽ phải thêm một bảng dẫn xuất khác để buộc @Rank đặt lại bằng 0 giữa hai bảng (và hy vọng trình tối ưu hóa không thay đổi thứ tự mà bảng đánh giá bảng hoặc sử dụng STRAIGHT_JOIN để ngăn chặn rằng):
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 |
+---------+------------+------+------+
Nhưng tối ưu hóa truy vấn này là khủng khiếp. Nó không thể sử dụng bất kỳ chỉ mục nào, nó tạo ra hai bảng tạm thời, sắp xếp chúng theo cách cứng và thậm chí sử dụng bộ đệm nối vì nó không thể sử dụng chỉ mục khi tham gia các bảng tạm thời. Đây là ví dụ đầu ra từ 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 |
+----+-------------+------------+--------+---------------+------+---------+------+------+---------------------------------+
Trong khi giải pháp của tôi sử dụng phép nối ngoài bên trái tối ưu hóa tốt hơn nhiều. Nó sử dụng không có bảng tạm thời và thậm chí báo cáo "Using index"
có nghĩa là nó có thể giải quyết việc tham gia bằng cách sử dụng chỉ số chỉ mục, mà không cần chạm vào dữ liệu.
+----+-------------+-------+------+---------------+---------+---------+-----------------+------+--------------------------+
| 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 |
+----+-------------+-------+------+---------------+---------+---------+-----------------+------+--------------------------+
Có thể bạn sẽ đọc những người khiếu nại trên blog của họ rằng "tham gia tạo SQL chậm", nhưng điều đó là vô nghĩa. Tối ưu hóa kém làm cho SQL chậm.
câu hỏi nâng cao hơn tại đây http://stackoverflow.com/questions/9841093/how-to-writegreatest-n-per-group-type-query-but-with-additional-conditions/9845109#9845109 – TMS