2009-05-29 26 views
45

Tôi có bảng sau:Chọn 3 kỷ lục gần đây nhất nơi các giá trị của một cột là khác biệt

id  time  text  otheridentifier 
    ------------------------------------------- 
    1  6   apple  4 
    2  7   orange 4 
    3  8   banana 3 
    4  9   pear  3 
    5  10  grape  2 

Những gì tôi muốn làm là chọn 3 kỷ lục gần đây nhất (theo thời gian, giảm dần), có otheridentifier s là khác biệt. Vì vậy, trong trường hợp này, kết quả sẽ là id 's: 5, 4 và 2.

id = 3 sẽ bị bỏ qua vì có hồ sơ gần đây hơn với cùng trường otheridentifier.

Đây là những gì tôi đã cố gắng để làm:

SELECT * FROM `table` GROUP BY (`otheridentifier`) ORDER BY `time` DESC LIMIT 3 

Tuy nhiên, tôi cuối cùng nhận được hàng id = 5, và thay vì 5, 4, 2 như mong đợi.

Ai đó có thể cho tôi biết tại sao truy vấn này không trả về những gì tôi mong đợi? Tôi đã thử thay đổi ORDER BY thành ASC nhưng điều này chỉ đơn giản là sắp xếp lại các hàng trả về thành 1, 3, 5.

Trả lời

34

Nó không trả về những gì bạn mong đợi vì nhóm xảy ra trước khi đặt hàng, như được phản ánh bởi vị trí của các mệnh đề trong Câu lệnh sql. Thật không may bạn sẽ phải có được fancier để có được những hàng bạn muốn. Hãy thử điều này:

SELECT * 
FROM `table` 
WHERE `id` = (
    SELECT `id` 
    FROM `table` as `alt` 
    WHERE `alt`.`otheridentifier` = `table`.`otheridentifier` 
    ORDER BY `time` DESC 
    LIMIT 1 
) 
ORDER BY `time` DESC 
LIMIT 3 
+1

tôi nhớ thời gian tôi dành nhiều thời gian để sửa chữa sql như thế này và quay ra mysql 4.0 không hỗ trợ truy vấn lồng nhau; p – Unreality

+1

@Unreality: Fortu nately hầu hết các giải pháp liên quan đến truy vấn con có thể được biểu diễn dưới dạng tham gia nếu cần. :) – Rytmis

+1

Vâng, nhưng những người có ORDER/LIMIT được _not_ dễ dàng biểu đạt bằng JOIN ... –

2
SELECT * FROM table t1 
WHERE t1.time = 
    (SELECT MAX(time) FROM table t2 
    WHERE t2.otheridentifier = t1.otheridentifier) 
+0

Làm cách nào để chọn hàng mới nhất cho mỗi trình nhận dạng khác? – Andomar

+0

@Andomar: Tôi không nên trả lời các câu hỏi khi tôi không hoàn toàn tỉnh táo. Thay đổi tên cột một chút - xem liệu nó có ý nghĩa hơn bây giờ không. :) – Rytmis

18

Bạn có thể tham gia vào bảng trên chính nó để lọc các mục cuối cùng mỗi otheridentifier, và sau đó lấy 3 hàng đầu đó:

SELECT last.* 
FROM `table` last 
LEFT JOIN `table` prev 
    ON prev.`otheridentifier` = last.`otheridentifier` 
    AND prev.`time` < last.`time` 
WHERE prev.`id` is null 
ORDER BY last.`time` DESC 
LIMIT 3 
+0

Andomar, bạn có thể giải thích cách MySQL đang sử dụng bảng 'prev' trong truy vấn của bạn không? Tôi đã cố gắng sử dụng điều này trên một truy vấn cục bộ và nhận được một lỗi nói rằng 'database.prev' không tồn tại. –

+0

Fiddle với dữ liệu của OP và truy vấn của bạn. Bạn không chắc chắn tại sao câu trả lời này được upvoted 6 lần - nó không hoạt động ở đây: http://sqlfiddle.com/#!2/ace0b/1 –

+1

@acoder: Bạn nói đúng, có một lỗi nhỏ trong truy vấn: tên bảng sau khi 'tham gia bên trái 'bị thiếu. Tôi đã cập nhật câu trả lời. – Andomar

2

Andomar's answer có lẽ là tốt nhất trong rằng nó không sử dụng truy vấn phụ.

Một cách tiếp cận khác:

select * 
from `table` t1 
where t1.`time` in (
        select max(s2.`time`) 
        from  `table` t2 
        group by t2.otheridentifier 
        ) 
+0

Tôi nghĩ rằng tôi thấy một vấn đề ở đây nếu các giá trị thời gian không phải là duy nhất - điều này có thể trả lại các hàng mà nó không nên. Giả sử có một giá trị thời gian là giá trị thời gian tối đa cho một trình nhận dạng khác, nhưng, có giá trị lớn thứ hai đối với một trình nhận dạng khác. Truy vấn này sẽ không trả về cả hai định danh khác? Mặc dù tôi có thể hoàn toàn bị mất, tôi vẫn hơi mệt. :) – Rytmis

+0

@Rytmis: Vâng, và truy vấn của tôi cũng như của bạn :) hehe – Andomar

+0

@Andomar: Hmm, bạn có chắc chắn về truy vấn của mình không? Bởi vì tôi chỉ thử nghiệm nó bằng cách thêm một hàng (6, 7, 'dâu tây', 3) - giá trị thời gian 7 là lớn nhất trong nhóm có bộ định danh khác 4, nhưng lớn thứ hai trong một có bộ định danh khác 3. My truy vấn vẫn chỉ trả về các hàng mà OP muốn. Trường hợp thử nghiệm của tôi có sai không? :) – Rytmis

1

gì về

SELECT *, max(time) FROM `table` group by otheridentifier 
+0

Chỉnh sửa - điều này hoạt động trên dữ liệu của OP. Tôi đã có thêm một số tham gia trong truy vấn của tôi. Điều này làm việc tốt với dữ liệu của OP. http://sqlfiddle.com/#!2/ace0b/2 –

+1

Dường như nó không hoạt động. Trong sqlfiddle của bạn, nó sẽ hiển thị màu cam và lê - giá trị thời gian của chúng cao hơn. – mahemoff

+0

@mahemoff Điều này không hoạt động chính xác như bạn đã nói. – GusDeCooL

4

Tôi đã có một yêu cầu tương tự, nhưng tôi có tiêu chí lựa chọn tiên tiến hơn. Sử dụng một số các câu trả lời khác mà tôi không thể có được chính xác những gì tôi cần, nhưng tôi thấy bạn vẫn có thể làm một GROUP BY sau và ORDER BY như thế này:

SELECT t.* FROM (SELECT * FROM table ORDER BY time DESC) t 
GROUP BY t.otheridentifier 
+0

hoạt động tốt cho tôi !! – wiLLiamcastrO

2

Bạn có thể sử dụng truy vấn này để có được câu trả lời đúng:

SELECT * FROM 
     (SELECT * FROM `table` order by time DESC) 
      t group by otheridentifier 
0

cũng này:

SELECT * FROM 
OrigTable T INNER JOIN 
( 
SELECT otheridentifier,max(time) AS duration 
FROM T 
GROUP BY otheridentifier) S 
ON S.duration = T.time AND S.otheridentifier = T.otheridentifier. 
Các vấn đề liên quan