Làm thế nào để tôi truy vấn sql cho một ngày kỷ lục mới nhất cho mỗi người dùng


228

Tôi có một bảng là một mục bộ sưu tập khi người dùng đăng nhập.

username, date,      value
--------------------------
brad,     1/2/2010,  1.1
fred,     1/3/2010,  1.0
bob,      8/4/2009,  1.5
brad,     2/2/2010,  1.2
fred,     12/2/2009, 1.3

etc..

Làm cách nào để tạo một truy vấn sẽ cung cấp cho tôi ngày mới nhất cho mỗi người dùng?

Cập nhật: Tôi quên rằng tôi cần phải có một giá trị đi cùng với ngày mới nhất.


7
Bạn đang dùng gói dữ liệu nào vậy? MySQL, SQL-Server, Oracle, ...?
Peter Lang

1
Bạn có cần giá trị đi cùng với ngày mới nhất hoặc giá trị tối đa VÀ ngày tối đa không?
Matthew Jones

Câu trả lời:


381
select t.username, t.date, t.value
from MyTable t
inner join (
    select username, max(date) as MaxDate
    from MyTable
    group by username
) tm on t.username = tm.username and t.date = tm.MaxDate

3
Khi làm việc với postgresql, phiên bản này có nhanh hơn sử dụng IN (truy vấn con) thay vì nối bên trong không?
TheOne

3
@TheOne như kinh nghiệm của tôi, sử dụng kết nối bên trong nhanh hơn trong điều kiện
dada

14
Cẩn thận với phương pháp này: nó có thể trả về nhiều hơn một hàng cho mỗi người dùng nếu họ có nhiều hơn một bản ghi mỗi ngày ( max(date)sẽ trả về một ngày sẽ tham gia nhiều bản ghi). Để tránh vấn đề này, tốt nhất nên sử dụng giải pháp của @ dotjoe: stackoverflow.com/a/2411763/4406793 .
Marco Roy

@RedFilter Điều này làm việc hoàn hảo cho vấn đề của tôi. Cảm ơn rất nhiều cho một truy vấn kỹ thuật như vậy. Nhân tiện, tôi đã sử dụng datetime thay vì ngày để tránh nhận được nhiều kết quả cho một ngày cụ thể
Muhammad Khan

tại sao bạn cần 'và t.date = tm.MaxDate' sẽ không đủ nhóm?
duldi

125

Sử dụng các hàm cửa sổ (hoạt động trong Oracle, Postgres 8.4, SQL Server 2005, DB2, Sybase, Firebird 3.0, MariaDB 10.3)

select * from (
    select
        username,
        date,
        value,
        row_number() over(partition by username order by date desc) as rn
    from
        yourtable
) t
where t.rn = 1

1
Đáng làm rõ sản phẩm / phiên bản Sybase nào. Nó không hoạt động trên Sybase ASE 16.
levant đâm vào

2
Một lợi ích lớn của phương pháp này là nó được đảm bảo chỉ luôn trả về một hàng cho mỗi phân vùng ( usernametrong trường hợp này) và thậm chí không yêu cầu trường "có thể đặt hàng" duy nhất (như tham gia vào max(date)các câu trả lời khác).
Marco Roy

1
Chỉ cần thêm một cái gì đó vào những gì @MarcoRoy đã nói, nếu bạn tình cờ có nhiều hơn một bản ghi có cùng ngày tối đa, nếu bạn thay đổi truy vấn, như khi bạn gỡ lỗi, một bản ghi khác có thể nhận được số hàng là 1, vì vậy kết quả có thể không nhất quán Nhưng miễn là bạn thực sự không quan tâm, thì đây không phải là vấn đề. Điều này có thể được giải quyết nếu bạn thêm PK sau ngày. Ví dụ : order by date desc, id desc).
Andrew

40

Tôi thấy hầu hết các nhà phát triển sử dụng truy vấn nội tuyến mà không xem xét tác động của nó đối với dữ liệu lớn.

Đơn giản, bạn có thể đạt được điều này bằng cách:

SELECT a.username, a.date, a.value
FROM myTable a
LEFT OUTER JOIN myTable b
ON a.username = b.username 
AND a.date < b.date
WHERE b.username IS NULL
ORDER BY a.date desc;

3
Trên thực tế, điều này chỉ hoạt động đối với các mục trùng lặp, nếu bạn có nhiều hơn 2 giá trị, điều kiện a.date <b.date không hoạt động, nghĩa là nó không phải là một giải pháp chung, mặc dù ý tưởng làm việc với LEFT OUTER THAM GIA là quan trọng điều trong câu trả lời này.
iversoncru

Thật thú vị, Sybase ASE 16 hoạt động tốt đối với các bảng nhỏ hơn (<10k hàng), nhưng với các bảng lớn hơn (> 100k hàng) thì nó bị treo ... Tôi nghĩ rằng đây sẽ là ví dụ hoàn hảo mà các DB quan hệ nên vượt trội ở ...
levant pied

1
@levantpied ... Yeah còn lại tham gia là tốn kém trên các bộ dữ liệu lớn hơn. Bạn có thể điều chỉnh hiệu suất bằng cách đặt điều kiện bộ lọc vào chính nó để xử lý nó theo một cách nào đó nếu có thể.
sujeet

21

Để có được toàn bộ hàng chứa ngày tối đa cho người dùng:

select username, date, value
from tablename where (username, date) in (
    select username, max(date) as date
    from tablename
    group by username
)

1
Làm việc cho MySQL
School Boy

1
Coi chừng việc này sẽ cung cấp cho bạn các bản sao nếu có nhiều hơn một bản ghi có cùng ngày cho một người dùng cụ thể. Bạn có thể hoặc không muốn điều này.
Andrew

Sql này chậm trong Oracle với mệnh đề, nó sẽ không sử dụng chỉ mục
cỏ

9
SELECT *     
FROM MyTable T1    
WHERE date = (
   SELECT max(date)
   FROM MyTable T2
   WHERE T1.username=T2.username
)

4
Mặc dù đây là một giải pháp khả thi khác, nhưng đây thường không phải là một cách tốt để giải quyết vấn đề này. Làm theo cách này sẽ khiến truy vấn bên trong chạy một lần cho mỗi tên trong bảng, gây ra sự chậm trễ lớn cho bất kỳ bảng nào có kích thước đáng kể. Thực hiện một truy vấn riêng không có phần tử từ truy vấn đầu tiên trong mệnh đề where sau đó có hai bảng được nối thường sẽ nhanh hơn.
Scott Chamberlain

Điều này có tính năng tốt là một trong những giải pháp dễ hiểu hơn mà không thực hiện cụ thể.
Michael Szczepaniak

7

Theo kinh nghiệm của tôi, cách nhanh nhất là lấy từng hàng mà không có hàng mới hơn trong bảng.

Một ưu điểm khác là cú pháp được sử dụng rất đơn giản và ý nghĩa của truy vấn khá dễ nắm bắt (lấy tất cả các hàng sao cho không có hàng mới hơn tồn tại cho tên người dùng được xem xét).

KHÔNG TỒN TẠI

SELECT username, value
FROM t
WHERE NOT EXISTS (
  SELECT *
  FROM t AS witness
  WHERE witness.username = t.username AND witness.date > t.date
);

SỐ LƯỢNG HÀNG

SELECT username, value
FROM (
  SELECT username, value, row_number() OVER (PARTITION BY username ORDER BY date DESC) AS rn
  FROM t
) t2
WHERE rn = 1

THAM GIA

SELECT t.username, t.value
FROM t
INNER JOIN (
  SELECT username, MAX(date) AS date
  FROM t
  GROUP BY username
) tm ON t.username = tm.username AND t.date = tm.date;

TRÁI PHIẾU THAM GIA

SELECT username, value
FROM t
LEFT OUTER JOIN t AS w ON t.username = w.username AND t.date < w.date
WHERE w.username IS NULL

Tôi đang gặp khó khăn để hiểu phiên bản KHÔNG EXISTS. Bạn không thiếu một tập hợp trong phần truy vấn phụ? Nếu tôi chạy cái này trên bàn, tôi chỉ lấy lại được 3 hồ sơ nhân viên từ 40 nhân viên mà tôi có trong bảng. Tôi sẽ nhận được ít nhất 40 hồ sơ. Trong truy vấn bên trong, chúng ta cũng không nên kết hợp theo tên người dùng?
Narshe

Nó hoạt động với tôi bằng cách sử dụng như sau:SELECT username, value FROM t WHERE NOT EXISTS ( SELECT * FROM t AS witness WHERE witness.date > t.date AND witness.username = t.username );
Narshe

Tôi đã xem xét các EXISTS và có vẻ như chỉ trả lại mục nhập cao hơn cho tất cả người dùng chứ không phải: "một truy vấn sẽ cung cấp cho tôi ngày mới nhất cho mỗi người dùng".
Tasos Zervos

Bạn thực sự đúng, tôi cập nhật truy vấn của tôi. Cảm ơn nhận xét của bạn! @Narshe xin lỗi tôi đã bỏ lỡ ý kiến ​​của bạn vì một số lý do: / Nhưng bạn hoàn toàn đúng.
Fabian Pijcke

2

Điều này sẽ cung cấp cho bạn kết quả chính xác cho câu hỏi đã chỉnh sửa của bạn.

Truy vấn phụ đảm bảo chỉ tìm các hàng của ngày mới nhất và bên ngoài GROUP BYsẽ chăm sóc các mối quan hệ. Khi có hai mục nhập cho cùng một ngày cho cùng một người dùng, nó sẽ trả về mục có giá trị cao nhất value.

SELECT t.username, t.date, MAX( t.value ) value
FROM your_table t
JOIN (
       SELECT username, MAX( date ) date
       FROM your_table
       GROUP BY username
) x ON ( x.username = t.username AND x.date = t.date )
GROUP BY t.username, t.date

1

Bạn cũng có thể sử dụng Chức năng xếp hạng phân tích

    with temp as 
(
select username, date, RANK() over (partition by username order by date desc) as rnk from t
)
select username, rnk from t where rnk = 1

0
SELECT Username, date, value
 from MyTable mt
 inner join (select username, max(date) date
              from MyTable
              group by username) sub
  on sub.username = mt.username
   and sub.date = mt.date

Sẽ giải quyết vấn đề cập nhật. Nó có thể không hoạt động tốt trên các bảng lớn, ngay cả với việc lập chỉ mục tốt.


0
SELECT *
FROM ReportStatus c
inner join ( SELECT 
  MAX(Date) AS MaxDate
  FROM ReportStatus ) m
on  c.date = m.maxdate

0

Đối với Oracle sắp xếp kết quả được thiết lập theo thứ tự giảm dần và lấy bản ghi đầu tiên, do đó bạn sẽ nhận được bản ghi mới nhất:

select * from mytable
where rownum = 1
order by date desc

0
SELECT DISTINCT Username, Dates,value 
FROM TableName
WHERE  Dates IN (SELECT  MAX(Dates) FROM TableName GROUP BY Username)


Username    Dates       value
bob         2010-02-02  1.2       
brad        2010-01-02  1.1       
fred        2010-01-03  1.0       

Điều này có thể sẽ không hoạt động nếu nhiều người dùng có đơn đặt hàng trong cùng một ngày; Nếu cả brad và bob đều có đơn hàng vào ngày 2 tháng 1 thì sao?
AHiggins

Tôi đang nhóm theo tên người dùng để nó hoạt động và kết quả sẽ như sau: Tên người dùng Giá trị ngày bob 2010/02/02 1.2 brad 2010/02/02 1.4 fred 2010-01-03 1.0
wara

0
SELECT t1.username, t1.date, value
FROM MyTable as t1
INNER JOIN (SELECT username, MAX(date)
            FROM MyTable
            GROUP BY username) as t2 ON  t2.username = t1.username AND t2.date = t1.date

4
Một hoặc hai câu về việc thực hiện hoặc giải thích đi một chặng đường dài hướng tới việc tạo ra một câu trả lời chất lượng.

0

Select * from table1 where lastest_date=(select Max(latest_date) from table1 where user=yourUserName)

Truy vấn bên trong sẽ trả về ngày mới nhất cho người dùng hiện tại, Truy vấn bên ngoài sẽ kéo tất cả dữ liệu theo kết quả truy vấn bên trong.


0

Tôi đã sử dụng cách này để lấy bản ghi cuối cùng cho mỗi người dùng mà tôi có trên bàn của mình. Đó là một truy vấn để có được vị trí cuối cùng cho nhân viên bán hàng theo thời gian gần đây được phát hiện trên các thiết bị PDA.

CREATE FUNCTION dbo.UsersLocation()
RETURNS TABLE
AS
RETURN
Select GS.UserID, MAX(GS.UTCDateTime) 'LastDate'
From USERGPS GS
where year(GS.UTCDateTime) = YEAR(GETDATE()) 
Group By GS.UserID
GO
select  gs.UserID, sl.LastDate, gs.Latitude , gs.Longitude
        from USERGPS gs
        inner join USER s on gs.SalesManNo = s.SalesmanNo 
        inner join dbo.UsersLocation() sl on gs.UserID= sl.UserID and gs.UTCDateTime = sl.LastDate 
        order by LastDate desc

0
SELECT * FROM TABEL1 WHERE DATE= (SELECT MAX(CREATED_DATE) FROM TABEL1)

Chào mừng bạn đến với StackOverflow và cảm ơn vì đã cố gắng giúp đỡ. Các câu trả lời chỉ có mã như của bạn ít được đánh giá cao khi so sánh với các câu trả lời giải thích cho giải pháp.
Yunnosch

Xin vui lòng đọc hướng dẫn cách trả lời này để cung cấp câu trả lời chất lượng.
thewewere

và. nó không trở về MAX cho mỗi tên người dùng, chỉ một hàng mới nhất.
IrvineCAGuy

0

Biên soạn nhỏ của tôi

  • tự join tốt hơn lồng nhauselect
  • nhưng group bykhông cho bạnprimary key cái nào thích hợp hơn chojoin
  • khóa này có thể được cung cấp bằng cách partition bykết hợp với first_value( tài liệu )

Vì vậy, đây là một truy vấn:

lựa chọn
 t. *
từ 
 Bảng t tham gia bên trong (
  chọn riêng biệt First_value (ID) trên (phân vùng theo thứ tự GroupColumn theo DateColumn desc) làm ID
  từ bảng
  trong đó FilterColumn = 'value'
 ) j trên t.ID = j.ID

Ưu điểm:

  • Lọc dữ liệu bằng wherecâu lệnh sử dụng bất kỳ cột nào
  • select bất kỳ cột nào từ các hàng được lọc

Nhược điểm:

  • Cần MS SQL Server bắt đầu từ năm 2012.

0

Tôi đã làm phần nào cho ứng dụng của mình vì nó:

Dưới đây là truy vấn:

select distinct i.userId,i.statusCheck, l.userName from internetstatus 
as i inner join login as l on i.userID=l.userID 
where nowtime in((select max(nowtime) from InternetStatus group by userID));    

0

Điều này tương tự với một trong những câu trả lời ở trên, nhưng theo tôi nó đơn giản và gọn gàng hơn rất nhiều. Ngoài ra, cho thấy một cách sử dụng tốt cho tuyên bố áp dụng chéo. Đối với SQL Server 2005 trở lên ...

select
    a.username,
    a.date,
    a.value,
from yourtable a
cross apply (select max(date) 'maxdate' from yourtable a1 where a.username=a1.username) b
where a.date=b.maxdate

0
SELECT MAX(DATE) AS dates 
FROM assignment  
JOIN paper_submission_detail ON  assignment.PAPER_SUB_ID = 
     paper_submission_detail.PAPER_SUB_ID 

1
Mặc dù mã này có thể giải quyết câu hỏi, bao gồm giải thích về cách thức và lý do giải quyết vấn đề này thực sự sẽ giúp cải thiện chất lượng bài đăng của bạn và có thể dẫn đến nhiều lượt bình chọn hơn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho độc giả trong tương lai, không chỉ người hỏi bây giờ. Vui lòng chỉnh sửa câu trả lời của bạn để thêm giải thích và đưa ra dấu hiệu về những hạn chế và giả định được áp dụng. Từ đánh giá
tiếng bíp đôi

-2

Điều này cũng sẽ hoạt động để có được tất cả các mục mới nhất cho người dùng.

SELECT username, MAX(date) as Date, value
FROM MyTable
GROUP BY username, value

1
Xin chào, cột giá trị cần phải nằm trong nhóm theo mệnh đề.
Juan Ruiz de Castilla

-4

Bạn sẽ sử dụng hàm tổng hợp MAX và GROUP BY

SELECT username, MAX(date), value FROM tablename GROUP BY username, value

7
Chỉnh sửa của bạn sẽ chỉ chọn ngẫu nhiên value, không chọn liên kết với MAX(date)hàng.
Alison R.

nó sẽ đưa ra ngày tối đa nhưng tên người dùng và giá trị có thể không cùng một bản ghi.
SKR
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.