2013-06-11 29 views
71

Tôi có một bảng ("lms_attendance") của người sử dụng check-in và ra lần đó trông như thế này:Chọn hàng với ngày gần đây nhất cho mỗi người dùng

id user time io (enum) 
1 9 1370931202 out 
2 9 1370931664 out 
3 6 1370932128 out 
4 12 1370932128 out 
5 12 1370933037 in 

Tôi đang cố gắng để tạo ra một cái nhìn của bảng này mà có đầu ra chỉ có hồ sơ gần đây nhất cho mỗi người dùng id, trong khi đem lại cho tôi sự "trong" hay giá trị "out", vì vậy cái gì đó như:

id user time io 
2 9 1370931664 out 
3 6 1370932128 out 
5 12 1370933037 in 

tôi khá chặt chẽ cho đến nay, nhưng tôi nhận ra rằng lượt xem sẽ không chấp nhận truy vấn phụ, điều này khiến việc này trở nên khó khăn hơn rất nhiều. Truy vấn gần nhất tôi nhận được:

select 
    `lms_attendance`.`id` AS `id`, 
    `lms_attendance`.`user` AS `user`, 
    max(`lms_attendance`.`time`) AS `time`, 
    `lms_attendance`.`io` AS `io` 
from `lms_attendance` 
group by 
    `lms_attendance`.`user`, 
    `lms_attendance`.`io` 

Nhưng những gì tôi nhận được là:

id user time io 
3 6 1370932128 out 
1 9 1370931664 out 
5 12 1370933037 in 
4 12 1370932128 out 

Đó là gần, nhưng không hoàn hảo. Tôi biết rằng nhóm cuối cùng bởi không nên ở đó, nhưng không có nó, nó trả về thời gian gần đây nhất, nhưng không phải với giá trị IO tương đối của nó.

Bất kỳ ý tưởng nào? Cảm ơn!

+0

bản sao có thể có của [How t o chọn tập hợp các bản ghi ngày gần đây nhất từ ​​một bảng mysql] (http://stackoverflow.com/questions/435703/how-to-select-the-most-recent-set-of-dated-records-from-a -mysql-table) – Barmar

+0

Quay lại hướng dẫn sử dụng. Bạn sẽ thấy rằng nó cung cấp các giải pháp cho vấn đề này cả các truy vấn phụ có và không có (tương quan và không được liên kết). – Strawberry

+0

@Barmar, về mặt kỹ thuật, như tôi đã chỉ ra trong câu trả lời của tôi, đây là bản sao của tất cả 700 câu hỏi với thẻ [tag: greatest-n-per-group]. – TMS

Trả lời

115

Query:

SQLFIDDLEExample

SELECT t1.* 
FROM lms_attendance t1 
WHERE t1.time = (SELECT MAX(t2.time) 
       FROM lms_attendance t2 
       WHERE t2.user = t1.user) 

Kết quả:

| ID | USER |  TIME | IO | 
-------------------------------- 
| 2 | 9 | 1370931664 | out | 
| 3 | 6 | 1370932128 | out | 
| 5 | 12 | 1370933037 | in | 

Giải pháp mà gon na việc mọi:

SQLFIDDLEExample

SELECT t1.* 
FROM lms_attendance t1 
WHERE t1.id = (SELECT t2.id 
       FROM lms_attendance t2 
       WHERE t2.user = t1.user    
       ORDER BY t2.id DESC 
       LIMIT 1) 
+2

wow! không chỉ làm việc này, tôi được phép tạo một khung nhìn với truy vấn này mặc dù nó chứa các truy vấn phụ. trước đây, khi tôi cố gắng tạo chế độ xem chứa các truy vấn phụ, nó không cho phép tôi. có quy định nào về lý do tại sao điều này được cho phép hay không, nhưng một quy tắc khác thì không? – Prodikl

+1

Tôi không biết :) có thể đã xảy ra sự cố với bí danh bảng ... – Justin

+0

rất lạ. cảm ơn một tấn! có lẽ đó là vì truy vấn phụ của tôi là một bảng giả mà tôi đã chọn FROM, trong ví dụ này được sử dụng trong mệnh đề WHERE. – Prodikl

-1

Có thể bạn có thể nhóm theo người dùng và sau đó đặt hàng theo thời gian desc. Một cái gì đó giống như dưới đây

SELECT * FROM lms_attendance group by user order by time desc; 
0
select b.* from 

    (select 
     `lms_attendance`.`user` AS `user`, 
     max(`lms_attendance`.`time`) AS `time` 
    from `lms_attendance` 
    group by 
     `lms_attendance`.`user`) a 

join 

    (select * 
    from `lms_attendance`) b 

on a.user = b.user 
and a.time = b.time 
+0

cảm ơn. tôi biết tôi có thể làm điều đó bằng cách sử dụng một truy vấn con, nhưng tôi đã hy vọng biến điều này thành một khung nhìn, và nó sẽ không cho phép truy vấn phụ trong khung nhìn AFAIK. tôi sẽ phải biến từng truy vấn phụ thành một chế độ xem, v.v ...? – Prodikl

+0

'tham gia (chọn * từ lms_attendance) b' =' tham gia lms_attendance b' – azerafati

47

Không cần phải cố gắng phát minh lại bánh xe, vì đây là chung greatest-n-per-group problem. Rất đẹp solution is presented.

Tôi thích giải pháp đơn giản nhất (see SQLFiddle, updated Justin's) mà không cần truy vấn con (do đó dễ dàng để sử dụng trong lượt xem):

SELECT t1.* 
FROM lms_attendance AS t1 
LEFT OUTER JOIN lms_attendance AS t2 
    ON t1.user = t2.user 
     AND (t1.time < t2.time 
     OR (t1.time = t2.time AND t1.Id < t2.Id)) 
WHERE t2.user IS NULL 

này cũng làm việc trong trường hợp có hai hồ sơ khác nhau với giá trị cao nhất cùng trong cùng một nhóm - nhờ thủ thuật với (t1.time = t2.time AND t1.Id < t2.Id). Tất cả những gì tôi đang làm ở đây là để đảm bảo rằng trong trường hợp khi hai hồ sơ của cùng một người dùng có cùng một thời gian chỉ có một được chọn. Không thực sự quan trọng nếu các tiêu chí là Id hoặc cái gì khác - về cơ bản bất kỳ tiêu chí được đảm bảo là duy nhất sẽ làm cho công việc ở đây.

+1

Sử dụng tối đa 't1.time t2.time', điều ngược lại với trực giác ban đầu của tôi. –

+1

@ J.Money vì ẩn ẩn phủ định: bạn chọn tất cả các bản ghi từ t1 mà * không có * bản ghi tương ứng từ t2 trong đó điều kiện 't1.time TMS

+2

' WHERE t2. người dùng IS NULL' hơi lạ. Dòng này có vai trò gì? –

4

Đã được giải quyết, nhưng chỉ để ghi lại, cách tiếp cận khác sẽ là tạo hai chế độ xem ...

CREATE TABLE lms_attendance 
(id int, user int, time int, io varchar(3)); 

CREATE VIEW latest_all AS 
SELECT la.user, max(la.time) time 
FROM lms_attendance la 
GROUP BY la.user; 

CREATE VIEW latest_io AS 
SELECT la.* 
FROM lms_attendance la 
JOIN latest_all lall 
    ON lall.user = la.user 
    AND lall.time = la.time; 

INSERT INTO lms_attendance 
VALUES 
(1, 9, 1370931202, 'out'), 
(2, 9, 1370931664, 'out'), 
(3, 6, 1370932128, 'out'), 
(4, 12, 1370932128, 'out'), 
(5, 12, 1370933037, 'in'); 

SELECT * FROM latest_io; 

Click here to see it in action at SQL Fiddle

+1

cảm ơn vì đã theo dõi! vâng, tôi sẽ tạo nhiều chế độ xem nếu không có cách nào dễ dàng hơn. cảm ơn một lần nữa – Prodikl

4

Có trụ sở tại câu trả lời @TMS, tôi thích nó vì không có nhu cầu truy vấn con nhưng tôi nghĩ rằng ommiting phần 'OR' sẽ đầy đủ và đơn giản hơn nhiều để hiểu và đọc.

SELECT t1.* 
FROM lms_attendance AS t1 
LEFT JOIN lms_attendance AS t2 
    ON t1.user = t2.user 
     AND t1.time < t2.time 
WHERE t2.user IS NULL 

nếu bạn không quan tâm đến hàng với thời gian rỗng, bạn có thể lọc chúng trong mệnh đề WHERE:

SELECT t1.* 
FROM lms_attendance AS t1 
LEFT JOIN lms_attendance AS t2 
    ON t1.user = t2.user 
     AND t1.time < t2.time 
WHERE t2.user IS NULL and t1.time IS NOT NULL 
+0

Bỏ qua phần 'OR' là một ý tưởng thực sự tồi nếu hai bản ghi có thể có cùng' thời gian'. – TMS

+0

Anf gì về hiệu suất? Dường như tạo bảng n * n. –

1

Hãy thử truy vấn này:

select id,user, max(time), io 
    FROM lms_attendance group by user; 
-1

này đã làm việc cho tôi:

SELECT user, time FROM 
(
    SELECT user, time FROM lms_attendance --where clause 
) AS T 
WHERE (SELECT COUNT(0) FROM table WHERE user = T.user AND time > T.time) = 0 
ORDER BY user ASC, time DESC 
Các vấn đề liên quan