Tìm nạp hàng có giá trị Max cho cột


574

Bàn:

UserId, Value, Date.

Tôi muốn nhận UserId, Giá trị tối đa (Ngày) cho mỗi UserId. Đó là, Giá trị cho mỗi UserId có ngày mới nhất. Có cách nào để làm điều này đơn giản trong SQL không? (Tốt nhất là Oracle)

Cập nhật: Xin lỗi vì bất kỳ sự mơ hồ nào: Tôi cần nhận TẤT CẢ các UserIds. Nhưng đối với mỗi UserId, chỉ có hàng đó mà người dùng đó có ngày mới nhất.


21
Điều gì xảy ra nếu có nhiều hàng có giá trị ngày tối đa cho một userid cụ thể?
David Aldridge

Các lĩnh vực chính của bảng là gì?
vamosrafa

một số giải pháp dưới đây được so sánh: sqlfiddle.com/#!4/6d4e81/1
Được sử dụng_By_Al sẵn

1
@DavidAldridge, Cột đó có khả năng là duy nhất.
Pacerier

Câu trả lời:


397

Điều này sẽ truy xuất tất cả các hàng mà giá trị cột my_date bằng với giá trị tối đa của my_date cho userid đó. Điều này có thể truy xuất nhiều hàng cho userid trong đó ngày tối đa nằm trên nhiều hàng.

select userid,
       my_date,
       ...
from
(
select userid,
       my_date,
       ...
       max(my_date) over (partition by userid) max_my_date
from   users
)
where my_date = max_my_date

"Chức năng phân tích đá"

Chỉnh sửa: Liên quan đến bình luận đầu tiên ...

"sử dụng truy vấn phân tích và tự tham gia đánh bại mục đích của truy vấn phân tích"

Không có tự tham gia trong mã này. Thay vào đó, có một vị từ được đặt trên kết quả của chế độ xem nội tuyến có chứa chức năng phân tích - một vấn đề rất khác và thực hành hoàn toàn tiêu chuẩn.

"Cửa sổ mặc định trong Oracle là từ hàng đầu tiên trong phân vùng đến cửa sổ hiện tại"

Mệnh đề cửa sổ chỉ được áp dụng với sự có mặt của mệnh đề theo mệnh đề. Không có thứ tự theo mệnh đề, không có mệnh đề cửa sổ nào được áp dụng theo mặc định và không có mệnh đề nào có thể được chỉ định rõ ràng.

Các mã hoạt động.


38
Khi được áp dụng cho một bảng có 8,8 triệu hàng, truy vấn này mất một nửa thời gian của các truy vấn trong một số câu trả lời được đánh giá cao khác.
Derek Mahar

4
Bất cứ ai quan tâm để gửi một liên kết đến MySQL tương đương với điều này, nếu có một liên kết?
hương thơm

2
Không thể trả lại trùng lặp này? Ví dụ. nếu hai hàng có cùng user_id và cùng ngày (điều này xảy ra là tối đa).
jastr

2
@jastr Tôi nghĩ rằng điều đó đã được thừa nhận trong câu hỏi
David Aldridge

3
Thay vì MAX(...) OVER (...)bạn cũng có thể sử dụng ROW_NUMBER() OVER (...)(cho nhóm đầu n-nhóm) hoặc RANK() OVER (...)(cho nhóm lớn nhất-n-nhóm).
MT0

441

Tôi thấy nhiều người sử dụng các truy vấn con hoặc các tính năng dành riêng cho nhà cung cấp để thực hiện việc này, nhưng tôi thường thực hiện loại truy vấn này mà không có truy vấn con theo cách sau. Nó sử dụng SQL đơn giản, chuẩn để nó hoạt động trong bất kỳ thương hiệu RDBMS nào.

SELECT t1.*
FROM mytable t1
  LEFT OUTER JOIN mytable t2
    ON (t1.UserId = t2.UserId AND t1."Date" < t2."Date")
WHERE t2.UserId IS NULL;

Nói cách khác: tìm nạp hàng từ t1nơi không có hàng nào khác tồn tại cùng ngày UserIdvà ngày lớn hơn.

(Tôi đặt mã định danh "Ngày" trong các dấu phân cách vì đó là từ dành riêng cho SQL.)

Trong trường hợp nếu t1."Date" = t2."Date", nhân đôi xuất hiện. Các bảng thường có auto_inc(seq)khóa, vd id. Để tránh nhân đôi có thể được sử dụng như sau:

SELECT t1.*
FROM mytable t1
  LEFT OUTER JOIN mytable t2
    ON t1.UserId = t2.UserId AND ((t1."Date" < t2."Date") 
         OR (t1."Date" = t2."Date" AND t1.id < t2.id))
WHERE t2.UserId IS NULL;

Nhận xét lại từ @Farhan:

Đây là một lời giải thích chi tiết hơn:

Một nỗ lực tham gia bên ngoài để tham gia t1với t2. Theo mặc định, tất cả các kết quả t1được trả về và nếu có kết quả khớp t2, nó cũng được trả về. Nếu không có kết quả trùng khớp t2cho một hàng nhất định t1, thì truy vấn vẫn trả về hàng đó t1và sử dụng NULLlàm trình giữ chỗ cho tất cả các t2cột. Đó chỉ là cách thức tham gia bên ngoài nói chung.

Thủ thuật trong truy vấn này là thiết kế điều kiện khớp của tham gia sao cho t2phải khớp như nhau useridlớn hơn date . Ý tưởng là nếu một hàng tồn tại trong t2đó có lớn hơn date, thì hàng trong t1đó được so sánh với không thể là lớn nhất datecho điều đó userid. Nhưng nếu không có kết quả khớp - tức là nếu không có hàng nào tồn tại t2với hàng lớn datehơn hàng trong t1- chúng ta biết rằng hàng trong đó t1là hàng có giá trị lớn nhất datecho hàng đã cho userid.

Trong những trường hợp đó (khi không có kết quả khớp), các cột t2sẽ là NULL- ngay cả các cột được chỉ định trong điều kiện nối. Vì vậy, đó là lý do chúng tôi sử dụng WHERE t2.UserId IS NULL, bởi vì chúng tôi đang tìm kiếm các trường hợp không có hàng đã được tìm thấy với một lớn hơn datecho trao userid.


7
Bill Bill. Đây là giải pháp sáng tạo nhất cho vấn đề này tôi từng thấy. Nó cũng khá hiệu quả trên tập dữ liệu khá lớn của tôi. Điều này chắc chắn đánh bại nhiều giải pháp khác mà tôi đã thấy hoặc những nỗ lực của riêng tôi trong việc giải quyết tình trạng khó khăn này.
Justin Noel

36
Khi được áp dụng cho một bảng có 8,8 triệu hàng, truy vấn này mất gần gấp đôi so với câu trả lời được chấp nhận.
Derek Mahar

16
@Derek: Tối ưu hóa phụ thuộc vào thương hiệu và phiên bản RDBMS, cũng như sự hiện diện của các chỉ mục, loại dữ liệu phù hợp, v.v.
Bill Karwin

7
Trên MySQL, loại truy vấn này dường như thực sự khiến nó lặp lại kết quả của phép nối Cartesian giữa các bảng, dẫn đến thời gian O (n ^ 2). Thay vào đó, sử dụng phương thức truy vấn con đã giảm thời gian truy vấn từ 2.0 xuống còn 0,003. YMMV.
Jesse

1
Có cách nào để điều chỉnh điều này để khớp với các hàng trong đó ngày là ngày lớn nhất nhỏ hơn hoặc bằng với ngày của người dùng không? Ví dụ: nếu người dùng đưa ra ngày "23-OCT-2011" và bảng bao gồm các hàng cho "24-OCT-2011", "22-OCT-2011", "20-OCT-2011", thì tôi muốn nhận "22 tháng 10 năm 2011". Đã gãi đầu và đọc đoạn trích này một lúc rồi ...
Cory Kendall

164
SELECT userid, MAX(value) KEEP (DENSE_RANK FIRST ORDER BY date DESC)
  FROM table
  GROUP BY userid

3
Trong các thử nghiệm của tôi bằng cách sử dụng bảng có số lượng hàng lớn, giải pháp này mất khoảng gấp đôi thời gian trong câu trả lời được chấp nhận.
Derek Mahar

7
Vui lòng cho xem bài kiểm tra của bạn
Rob van Wijk

Tôi xác nhận nó nhanh hơn nhiều so với các giải pháp khác
tamersalama

5
rắc rối là nó không trả lại bản ghi đầy đủ
Được sử dụng_By_Al sẵn

@ user2067753 Không, nó không trả về bản ghi đầy đủ. Bạn có thể sử dụng cùng một biểu thức MAX () .. KEEP .. trên nhiều cột, vì vậy bạn có thể chọn tất cả các cột bạn cần. Nhưng thật bất tiện nếu bạn muốn có một số lượng lớn các cột và muốn sử dụng CHỌN *.
Dave Costa

51

Tôi không biết tên cột chính xác của bạn, nhưng nó sẽ giống như thế này:

    chọn userid, giá trị
      từ người dùng u1
     trong đó ngày = (chọn tối đa (ngày)
                     từ người dùng u2
                    trong đó u1.userid = u2.userid)

3
Có lẽ không hiệu quả lắm, Steve.
David Aldridge

7
Có lẽ bạn đang đánh giá thấp trình tối ưu hóa truy vấn của Oracle.
Rafał Dowgird

3
Không có gì. Điều này gần như chắc chắn sẽ được thực hiện dưới dạng quét toàn bộ với phép nối vòng lặp lồng nhau để lấy ngày. Bạn đang nói về logic của io theo thứ tự gấp 4 lần số lượng hàng trong bảng và đáng sợ với số lượng dữ liệu không tầm thường.
David Aldridge

4
FYI, "Không hiệu quả, nhưng hoạt động" giống như "Hoạt động, nhưng không hiệu quả". Khi nào chúng ta từ bỏ hiệu quả như một mục tiêu thiết kế?
David Aldridge

6
+1 vì khi dữ liệu của bạn không dài hàng triệu hàng, đây là giải pháp dễ hiểu nhất. khi bạn có nhiều nhà phát triển thuộc tất cả các cấp độ kỹ năng sửa đổi mã, mức độ dễ hiểu là quan trọng hơn một phần giây trong hiệu suất không thể nhận thấy.
n00b

35

Không làm việc, tôi không có Oracle để xử lý, nhưng tôi dường như nhớ lại rằng Oracle cho phép nhiều cột được khớp trong mệnh đề IN, điều này ít nhất nên tránh các tùy chọn sử dụng truy vấn con tương quan, điều này hiếm khi tốt ý tưởng.

Một cái gì đó như thế này, có lẽ (không thể nhớ nếu danh sách cột nên được ngoặc đơn hay không):

SELECT * 
FROM MyTable
WHERE (User, Date) IN
  ( SELECT User, MAX(Date) FROM MyTable GROUP BY User)

EDIT: Chỉ cần thử nó thực sự:

SQL> create table MyTable (usr char(1), dt date);
SQL> insert into mytable values ('A','01-JAN-2009');
SQL> insert into mytable values ('B','01-JAN-2009');
SQL> insert into mytable values ('A', '31-DEC-2008');
SQL> insert into mytable values ('B', '31-DEC-2008');
SQL> select usr, dt from mytable
  2  where (usr, dt) in 
  3  ( select usr, max(dt) from mytable group by usr)
  4  /

U DT
- ---------
A 01-JAN-09
B 01-JAN-09

Vì vậy, nó hoạt động, mặc dù một số công cụ mới được đề cập ở nơi khác có thể hiệu quả hơn.


4
Điều này cũng hoạt động độc đáo trên PostgreSQL. Và tôi thích sự đơn giản và tổng quát của nó - truy vấn con nói "Đây là tiêu chí của tôi", truy vấn bên ngoài nói "Và đây là chi tiết tôi muốn xem". +1.
j_random_hacker

13

Tôi biết bạn đã yêu cầu Oracle, nhưng trong SQL 2005 chúng tôi hiện sử dụng điều này:


-- Single Value
;WITH ByDate
AS (
SELECT UserId, Value, ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY Date DESC) RowNum
FROM UserDates
)
SELECT UserId, Value
FROM ByDate
WHERE RowNum = 1

-- Multiple values where dates match
;WITH ByDate
AS (
SELECT UserId, Value, RANK() OVER (PARTITION BY UserId ORDER BY Date DESC) Rnk
FROM UserDates
)
SELECT UserId, Value
FROM ByDate
WHERE Rnk = 1

7

Tôi không có Oracle để kiểm tra nó, nhưng giải pháp hiệu quả nhất là sử dụng các truy vấn phân tích. Nó sẽ trông giống như thế này:

SELECT DISTINCT
    UserId
  , MaxValue
FROM (
    SELECT UserId
      , FIRST (Value) Over (
          PARTITION BY UserId
          ORDER BY Date DESC
        ) MaxValue
    FROM SomeTable
  )

Tôi nghi ngờ rằng bạn có thể thoát khỏi truy vấn bên ngoài và đặt khác biệt vào bên trong, nhưng tôi không chắc chắn. Trong khi đó tôi biết cái này hoạt động.

Nếu bạn muốn tìm hiểu về các truy vấn phân tích, tôi khuyên bạn nên đọc http://www.orafaq.com/node/55http://www.akadia.com/service/ora_analytic_fifts.html . Dưới đây là tóm tắt ngắn.

Trong các truy vấn phân tích mui xe sắp xếp toàn bộ dữ liệu, sau đó xử lý tuần tự. Khi bạn xử lý nó, bạn phân vùng tập dữ liệu theo các tiêu chí nhất định và sau đó cho mỗi hàng nhìn vào một số cửa sổ (mặc định là giá trị đầu tiên trong phân vùng cho hàng hiện tại - mặc định đó cũng hiệu quả nhất) và có thể tính toán các giá trị bằng cách sử dụng số lượng các hàm phân tích (danh sách này rất giống với các hàm tổng hợp).

Trong trường hợp này đây là những gì truy vấn bên trong làm. Toàn bộ dữ liệu được sắp xếp theo UserId rồi Date DESC. Sau đó, nó xử lý nó trong một lần. Đối với mỗi hàng, bạn trả về UserId và Ngày đầu tiên được nhìn thấy cho UserId đó (vì ngày được sắp xếp DESC, đó là ngày tối đa). Điều này cung cấp cho bạn câu trả lời của bạn với các hàng trùng lặp. Sau đó, các DISTINCT bên ngoài trùng lặp.

Đây không phải là một ví dụ đặc biệt ngoạn mục của các truy vấn phân tích. Đối với một chiến thắng lớn hơn nhiều, hãy xem xét lấy một bảng các biên lai tài chính và tính toán cho mỗi người dùng và biên lai, tổng cộng số tiền họ đã trả. Phân tích truy vấn giải quyết mà hiệu quả. Các giải pháp khác là ít hiệu quả. Đó là lý do tại sao chúng là một phần của tiêu chuẩn SQL 2003. (Thật không may, Postgres chưa có chúng. Grrr ...)


Bạn cũng cần trả về giá trị ngày để trả lời hoàn toàn câu hỏi. Nếu điều đó có nghĩa là một mệnh đề First_value khác thì tôi đề nghị rằng giải pháp phức tạp hơn mức cần thiết và phương pháp phân tích dựa trên max (ngày) đọc tốt hơn.
David Aldridge

Các câu hỏi không nói gì về việc trả lại ngày. Bạn có thể làm điều đó bằng cách thêm FIRST (Ngày) khác hoặc chỉ bằng cách truy vấn Ngày và thay đổi truy vấn bên ngoài thành NHÓM THEO. Tôi sẽ sử dụng cái đầu tiên và mong đợi trình tối ưu hóa tính toán cả hai trong một lần.
dùng11318

"Câu hỏi không nói gì về việc trả lại ngày" ... vâng, bạn đúng. Lấy làm tiếc. Nhưng việc thêm nhiều mệnh đề FIRST_VALUE sẽ trở nên lộn xộn khá nhanh. Đó là một loại cửa sổ duy nhất, nhưng nếu bạn có 20 cột để trả về hàng đó thì bạn đã viết rất nhiều mã để lội qua.
David Aldridge

Nó cũng xảy ra với tôi rằng giải pháp này không xác định đối với dữ liệu trong đó một userid có nhiều hàng có ngày tối đa và các GIÁ TRỊ khác nhau. Nhiều lỗi trong câu hỏi hơn là câu trả lời.
David Aldridge

1
Tôi đồng ý nó là dài dòng đau đớn. Tuy nhiên, đó thường không phải là trường hợp với SQL? Và bạn đúng rằng giải pháp này không mang tính quyết định. Có nhiều cách để giải quyết các mối quan hệ, và đôi khi mỗi cách là những gì bạn muốn.
dùng11318

6

Một điều khoản CHẤT LƯỢNG sẽ không đơn giản và tốt nhất?

select userid, my_date, ...
from users
qualify rank() over (partition by userid order by my_date desc) = 1

Đối với ngữ cảnh, trên Teradata ở đây, một bài kiểm tra kích thước khá của bản này chạy trong 17 giây với phiên bản CHẤT LƯỢNG này và trong 23 giây với 'chế độ xem nội tuyến' / giải pháp Aldridge # 1.


1
Đây là câu trả lời tốt nhất theo ý kiến ​​của tôi. Tuy nhiên, hãy cẩn thận với rank()chức năng trong các tình huống có mối quan hệ. Bạn có thể kết thúc với nhiều hơn một rank=1. Tốt hơn để sử dụng row_number()nếu bạn thực sự muốn chỉ một bản ghi được trả lại.
cartb Beforehorse

1
Ngoài ra, hãy lưu ý rằng QUALIFYmệnh đề này dành riêng cho Teradata. Trong Oracle (ít nhất), bạn phải lồng truy vấn và bộ lọc của mình bằng cách sử dụng một WHEREmệnh đề trên câu lệnh chọn gói (có thể đạt hiệu suất một chạm, tôi tưởng tượng).
cartb Beforehorse

5

Trong Oracle 12c+, bạn có thể sử dụng các truy vấn Top n cùng với chức năng phân tích rankđể đạt được điều này rất chính xác mà không cần truy vấn con:

select *
from your_table
order by rank() over (partition by user_id order by my_date desc)
fetch first 1 row with ties;

Ở trên trả về tất cả các hàng với max my_date cho mỗi người dùng.

Nếu bạn chỉ muốn một hàng có ngày tối đa, thì hãy thay thế rankbằng row_number:

select *
from your_table
order by row_number() over (partition by user_id order by my_date desc)
fetch first 1 row with ties; 

5

Sử dụng ROW_NUMBER()để chỉ định một thứ hạng duy nhất về giảm dần Datecho mỗi UserId, sau đó lọc vào hàng đầu tiên cho mỗi UserId(nghĩa là ROW_NUMBER= 1).

SELECT UserId, Value, Date
FROM (SELECT UserId, Value, Date,
        ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY Date DESC) rn
      FROM users) u
WHERE rn = 1;

5

Với PostgreSQL 8.4 trở lên, bạn có thể sử dụng:

select user_id, user_value_1, user_value_2
  from (select user_id, user_value_1, user_value_2, row_number()
          over (partition by user_id order by user_date desc) 
        from users) as r
  where r.row_number=1

3

Tôi điều bạn đã thực hiện biến thể này cho truy vấn trước đó:

SELECT UserId, Value FROM Users U1 WHERE 
Date = ( SELECT MAX(Date)    FROM Users where UserId = U1.UserId)

3
Select  
   UserID,  
   Value,  
   Date  
From  
   Table,  
   (  
      Select  
          UserID,  
          Max(Date) as MDate  
      From  
          Table  
      Group by  
          UserID  
    ) as subQuery  
Where  
   Table.UserID = subQuery.UserID and  
   Table.Date = subQuery.mDate  

3

Chỉ cần viết một ví dụ "sống" tại nơi làm việc :)

Cái này hỗ trợ nhiều giá trị cho UserId trong cùng một ngày.

Cột: UserId, Giá trị, Ngày

SELECT
   DISTINCT UserId,
   MAX(Date) OVER (PARTITION BY UserId ORDER BY Date DESC),
   MAX(Values) OVER (PARTITION BY UserId ORDER BY Date DESC)
FROM
(
   SELECT UserId, Date, SUM(Value) As Values
   FROM <<table_name>>
   GROUP BY UserId, Date
)

Bạn có thể sử dụng FIRST_VALUE thay vì MAX và tra cứu nó trong kế hoạch giải thích. Tôi không có thời gian để chơi với nó.

Tất nhiên, nếu tìm kiếm thông qua các bảng lớn, có thể tốt hơn nếu bạn sử dụng gợi ý ĐẦY ĐỦ trong truy vấn của mình.


3
select VALUE from TABLE1 where TIME = 
   (select max(TIME) from TABLE1 where DATE= 
   (select max(DATE) from TABLE1 where CRITERIA=CRITERIA))

2

Tôi nghĩ một cái gì đó như thế này. (Hãy tha thứ cho tôi về bất kỳ lỗi cú pháp nào; Tôi đã quen với việc sử dụng HQL tại thời điểm này!)

EDIT: Cũng đọc sai câu hỏi! Đã sửa truy vấn ...

SELECT UserId, Value
FROM Users AS user
WHERE Date = (
    SELECT MAX(Date)
    FROM Users AS maxtest
    WHERE maxtest.UserId = user.UserId
)

Không đáp ứng điều kiện "cho mỗi UserId"
David Aldridge

Nó sẽ thất bại ở đâu? Đối với mỗi UserID trong Người dùng, sẽ được đảm bảo rằng ít nhất một hàng có chứa UserID đó sẽ được trả về. Hay tôi đang thiếu một trường hợp đặc biệt ở đâu đó?
jdmichal

2

(T-SQL) Trước tiên hãy lấy tất cả người dùng và maxdate của họ. Tham gia với bảng để tìm các giá trị tương ứng cho người dùng trên maxdates.

create table users (userid int , value int , date datetime)
insert into users values (1, 1, '20010101')
insert into users values (1, 2, '20020101')
insert into users values (2, 1, '20010101')
insert into users values (2, 3, '20030101')

select T1.userid, T1.value, T1.date 
    from users T1,
    (select max(date) as maxdate, userid from users group by userid) T2    
    where T1.userid= T2.userid and T1.date = T2.maxdate

các kết quả:

userid      value       date                                    
----------- ----------- -------------------------- 
2           3           2003-01-01 00:00:00.000
1           2           2002-01-01 00:00:00.000

2

Câu trả lời ở đây chỉ có Oracle. Đây là một câu trả lời phức tạp hơn một chút trong tất cả SQL:

Ai có kết quả bài tập về nhà tốt nhất (tổng điểm tối đa của bài tập về nhà)?

SELECT FIRST, LAST, SUM(POINTS) AS TOTAL
FROM STUDENTS S, RESULTS R
WHERE S.SID = R.SID AND R.CAT = 'H'
GROUP BY S.SID, FIRST, LAST
HAVING SUM(POINTS) >= ALL (SELECT SUM (POINTS)
FROM RESULTS
WHERE CAT = 'H'
GROUP BY SID)

Và một ví dụ khó khăn hơn, cần một số lời giải thích, mà tôi không có thời gian:

Đưa ra cuốn sách (ISBN và tiêu đề) phổ biến nhất trong năm 2008, tức là được mượn nhiều nhất trong năm 2008.

SELECT X.ISBN, X.title, X.loans
FROM (SELECT Book.ISBN, Book.title, count(Loan.dateTimeOut) AS loans
FROM CatalogEntry Book
LEFT JOIN BookOnShelf Copy
ON Book.bookId = Copy.bookId
LEFT JOIN (SELECT * FROM Loan WHERE YEAR(Loan.dateTimeOut) = 2008) Loan 
ON Copy.copyId = Loan.copyId
GROUP BY Book.title) X
HAVING loans >= ALL (SELECT count(Loan.dateTimeOut) AS loans
FROM CatalogEntry Book
LEFT JOIN BookOnShelf Copy
ON Book.bookId = Copy.bookId
LEFT JOIN (SELECT * FROM Loan WHERE YEAR(Loan.dateTimeOut) = 2008) Loan 
ON Copy.copyId = Loan.copyId
GROUP BY Book.title);

Hy vọng điều này sẽ giúp (bất cứ ai) .. :)

Trân trọng, Guus


Câu trả lời được chấp nhận không phải là "Chỉ dành cho Oracle" - đó là SQL tiêu chuẩn (được nhiều DBMS hỗ trợ)
a_horse_with_no_name

2

Ngày giả định là duy nhất cho một ID người dùng cụ thể, đây là một số TSQL:

SELECT 
    UserTest.UserID, UserTest.Value
FROM UserTest
INNER JOIN
(
    SELECT UserID, MAX(Date) MaxDate
    FROM UserTest
    GROUP BY UserID
) Dates
ON UserTest.UserID = Dates.UserID
AND UserTest.Date = Dates.MaxDate 

2

Tôi đến bữa tiệc khá muộn nhưng lần hack sau sẽ vượt trội hơn cả các truy vấn con tương quan và bất kỳ chức năng phân tích nào nhưng có một hạn chế: các giá trị phải chuyển đổi thành chuỗi. Vì vậy, nó hoạt động cho ngày, số và các chuỗi khác. Mã trông không đẹp nhưng hồ sơ thực thi là tuyệt vời.

select
    userid,
    to_number(substr(max(to_char(date,'yyyymmdd') || to_char(value)), 9)) as value,
    max(date) as date
from 
    users
group by
    userid

Lý do tại sao mã này hoạt động tốt là vì nó chỉ cần quét bảng một lần. Nó không yêu cầu bất kỳ chỉ mục nào và quan trọng nhất là không cần sắp xếp bảng, điều mà hầu hết các hàm phân tích đều làm. Các chỉ mục sẽ giúp mặc dù nếu bạn cần lọc kết quả cho một userid.


Đó là một kế hoạch thực hiện tốt so với hầu hết, nhưng áp dụng tất cả các thủ thuật đó cho nhiều hơn thì một vài lĩnh vực sẽ tẻ nhạt và có thể làm việc chống lại nó. Nhưng rất thú vị - cảm ơn. xem sqlfiddle.com/#!4/2749b5/23
Được sử dụng_By_Al sẵn

Bạn đúng nó có thể trở nên tẻ nhạt, đó là lý do tại sao điều này chỉ nên được thực hiện khi hiệu năng của truy vấn yêu cầu. Đó thường là trường hợp với các kịch bản ETL.
aLevelOfInirection

cái này rất đẹp đã làm một cái gì đó tương tự bằng cách sử dụng LISTAGG nhưng trông xấu xí. postgres có một altenative tốt hơn bằng cách sử dụng Array_agg. xem câu trả lời của tôi :)
Bruno Calza

1
select userid, value, date
  from thetable t1 ,
       ( select t2.userid, max(t2.date) date2 
           from thetable t2 
          group by t2.userid ) t3
 where t3.userid t1.userid and
       t3.date2 = t1.date

IMHO này hoạt động. HTH


1

Tôi nghĩ rằng điều này sẽ làm việc?

Select
T1.UserId,
(Select Top 1 T2.Value From Table T2 Where T2.UserId = T1.UserId Order By Date Desc) As 'Value'
From
Table T1
Group By
T1.UserId
Order By
T1.UserId

1

Lần thử đầu tiên tôi đã đọc sai câu hỏi, theo câu trả lời hàng đầu, đây là một ví dụ hoàn chỉnh với kết quả chính xác:

CREATE TABLE table_name (id int, the_value varchar(2), the_date datetime);

INSERT INTO table_name (id,the_value,the_date) VALUES(1 ,'a','1/1/2000');
INSERT INTO table_name (id,the_value,the_date) VALUES(1 ,'b','2/2/2002');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'c','1/1/2000');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'d','3/3/2003');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'e','3/3/2003');

-

  select id, the_value
      from table_name u1
      where the_date = (select max(the_date)
                     from table_name u2
                     where u1.id = u2.id)

-

id          the_value
----------- ---------
2           d
2           e
1           b

(3 row(s) affected)

1

Điều này cũng sẽ chăm sóc các bản sao (trả về một hàng cho mỗi user_id):

SELECT *
FROM (
  SELECT u.*, FIRST_VALUE(u.rowid) OVER(PARTITION BY u.user_id ORDER BY u.date DESC) AS last_rowid
  FROM users u
) u2
WHERE u2.rowid = u2.last_rowid

1

Chỉ cần thử nghiệm điều này và nó dường như hoạt động trên một bảng ghi nhật ký

select ColumnNames, max(DateColumn) from log  group by ColumnNames order by 1 desc

1

Điều này nên đơn giản như:

SELECT UserId, Value
FROM Users u
WHERE Date = (SELECT MAX(Date) FROM Users WHERE UserID = u.UserID)

1

Giải pháp cho MySQL không có khái niệm phân vùng KEEP, DENSE_RANK.

select userid,
       my_date,
       ...
from
(
select @sno:= case when @pid<>userid then 0
                    else @sno+1
    end as serialnumber, 
    @pid:=userid,
       my_Date,
       ...
from   users order by userid, my_date
) a
where a.serialnumber=0

Tham khảo: http://benincampus.blogspot.com/2013/08/select-rows-which-have-maxmin-value-in.html


Điều này không hoạt động " trên các DB khác ". Điều này chỉ hoạt động trên MySQL và có thể trên SQL Server vì nó có khái niệm biến tương tự. Nó chắc chắn sẽ không hoạt động trên Oracle, Postgres, DB2, Derby, H2, HSQLDB, Vertica, Greenplum. Ngoài ra, câu trả lời được chấp nhận là ANSI SQL tiêu chuẩn (chỉ biết rằng MySQL không hỗ trợ)
a_horse_with_no_name

ngựa, tôi đoán bạn đúng Tôi không có kiến ​​thức về các DB khác, hoặc ANSI. Giải pháp của tôi có thể giải quyết vấn đề trong MySQL, không có hỗ trợ thích hợp cho ANSI SQL để giải quyết vấn đề theo cách tiêu chuẩn.
Ben Lin

1

Nếu bạn đang sử dụng Postgres, bạn có thể sử dụng array_aggnhư

SELECT userid,MAX(adate),(array_agg(value ORDER BY adate DESC))[1] as value
FROM YOURTABLE
GROUP BY userid

Tôi không quen thuộc với Oracle. Đây là những gì tôi nghĩ ra

SELECT 
  userid,
  MAX(adate),
  SUBSTR(
    (LISTAGG(value, ',') WITHIN GROUP (ORDER BY adate DESC)),
    0,
    INSTR((LISTAGG(value, ',') WITHIN GROUP (ORDER BY adate DESC)), ',')-1
  ) as value 
FROM YOURTABLE
GROUP BY userid 

Cả hai truy vấn trả về kết quả giống như câu trả lời được chấp nhận. Xem SQLFiddles:

  1. Câu trả lời được chấp nhận
  2. Giải pháp của tôi với Postgres
  3. Giải pháp của tôi với Oracle

0

Nếu (UserID, Ngày) là duy nhất, tức là không có ngày nào xuất hiện hai lần cho cùng một người dùng thì:

select TheTable.UserID, TheTable.Value
from TheTable inner join (select UserID, max([Date]) MaxDate
                          from TheTable
                          group by UserID) UserMaxDate
     on TheTable.UserID = UserMaxDate.UserID
        TheTable.[Date] = UserMaxDate.MaxDate;

Tôi tin rằng bạn cũng cần tham gia UserID
Tom H

0
select   UserId,max(Date) over (partition by UserId) value from users;

2
Điều này sẽ trả về tất cả các hàng, không chỉ một hàng cho mỗi người dùng.
Jon Heller
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.