bagaimana cara meminta sql untuk tanggal catatan terbaru untuk setiap pengguna


228

Saya memiliki tabel yang merupakan entri koleksi ketika pengguna login.

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..

Bagaimana cara membuat kueri yang akan memberi saya tanggal terbaru untuk setiap pengguna?

Pembaruan: Saya lupa bahwa saya perlu memiliki nilai yang sesuai dengan tanggal terbaru.


7
Database apa yang Anda gunakan? MySQL, SQL-Server, Oracle, ...?
Peter Lang

1
Apakah Anda memerlukan nilai yang sesuai dengan tanggal terbaru, atau nilai maksimum DAN tanggal maksimum?
Matthew Jones

Jawaban:


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
Ketika bekerja dengan postgresql, apakah versi ini lebih cepat daripada menggunakan IN (subquery) alih-alih gabungan bagian dalam?
TheOne

3
@TheOne sebagai pengalaman saya, menggunakan inner join lebih cepat daripada dalam kondisi
dada

14
Hati-hati dengan pendekatan ini: ia dapat mengembalikan lebih dari satu baris per pengguna jika mereka memiliki lebih dari satu catatan per tanggal ( max(date)akan mengembalikan tanggal yang akan bergabung dengan banyak catatan). Untuk menghindari masalah ini, akan lebih baik menggunakan solusi @ dotjoe: stackoverflow.com/a/2411763/4406793 .
Marco Roy

@RedFilter Ini berfungsi sempurna untuk masalah saya. Terima kasih banyak untuk permintaan teknis seperti itu. Ngomong-ngomong, saya menggunakan datetime alih-alih tanggal untuk menghindari mendapatkan beberapa hasil untuk tanggal tertentu
Muhammad Khan

mengapa Anda memerlukan 'dan t.date = tm.MaxDate' tidak akan cukup?
duldi

125

Menggunakan fungsi jendela (berfungsi di 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
Layak mengklarifikasi produk / versi Sybase mana. Itu tidak berfungsi pada Sybase ASE 16.
levant pied

2
Manfaat besar dari pendekatan ini adalah bahwa ia dijamin untuk selalu mengembalikan hanya satu baris per partisi ( username, dalam hal ini) dan bahkan tidak memerlukan bidang "pesanan" yang unik (seperti bergabung max(date)dalam jawaban lain).
Marco Roy

1
Hanya untuk menambahkan sesuatu ke apa yang dikatakan @MarcoRoy, jika Anda memiliki lebih dari satu catatan dengan tanggal maks yang sama, jika Anda mengubah kueri, seperti ketika Anda men-debug-nya, catatan yang berbeda mungkin menerima nomor baris 1, jadi hasilnya mungkin tidak konsisten. Tetapi selama Anda benar-benar tidak peduli, maka ini seharusnya tidak menjadi masalah. Ini bisa diselesaikan jika Anda menambahkan PK setelah tanggal. Sebagai contoh: order by date desc, id desc).
Andrew

40

Saya melihat sebagian besar pengembang menggunakan kueri sebaris tanpa mempertimbangkan dampaknya pada data yang sangat besar.

Cukup, Anda dapat mencapai ini dengan:

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
sebenarnya ini hanya berfungsi untuk duplikat, jika Anda memiliki lebih dari 2 nilai, kondisi a.tanggal <b.tanggal tidak berfungsi, artinya, itu bukan solusi umum, meskipun gagasan bekerja dengan LEFT OUTER JOIN adalah yang penting hal dalam jawaban ini.
iversoncru

Yang cukup menarik, Sybase ASE 16 berfungsi dengan baik untuk tabel yang lebih kecil (<10k baris), tetapi dengan yang lebih besar (> 100k baris) sangat bagus ... Saya pikir ini akan menjadi contoh sempurna DB relasional yang harus unggul di ...
levant pied

1
@levantpied ... Gabung kiri mahal pada set data yang lebih besar. Anda dapat mengubah kinerja dengan menempatkan kondisi filter pada join sendiri untuk mengatasinya jika mungkin.
sujeet

21

Untuk mendapatkan seluruh baris yang berisi tanggal maks untuk pengguna:

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

1
Bekerja untuk MySQL
School Boy

1
Hati-hati karena ini akan memberi Anda duplikat jika ada lebih dari satu catatan dengan tanggal yang sama untuk pengguna tertentu. Anda mungkin atau mungkin tidak menginginkan ini.
Andrew

Sql ini lambat di Oracle dengan dalam klausa, itu tidak akan menggunakan indeks
meadlai

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

4
Meskipun ini adalah solusi lain yang mungkin, ini biasanya bukan cara yang baik untuk menyelesaikan ini. Melakukannya dengan cara ini akan menyebabkan permintaan dalam berjalan sekali untuk setiap nama dalam tabel, menyebabkan perlambatan besar untuk setiap tabel dengan ukuran signifikan. Melakukan kueri terpisah yang tidak memiliki elemen dari kueri pertama di mana klausa kemudian menggabungkan dua tabel biasanya akan lebih cepat.
Scott Chamberlain

Ini memang memiliki fitur yang bagus untuk menjadi salah satu solusi yang lebih dimengerti yang tidak spesifik implementasi.
Michael Szczepaniak

7

Dari pengalaman saya, cara tercepat adalah mengambil setiap baris yang tidak ada baris baru dalam tabel.

Keuntungan lain adalah bahwa sintaks yang digunakan sangat sederhana, dan bahwa makna kueri agak mudah untuk dipahami (ambil semua baris sehingga tidak ada baris baru untuk nama pengguna yang dipertimbangkan).

TIDAK ADA

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

ROW_NUMBER

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

INNER BERGABUNG

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;

LEFT OUTER BERGABUNG

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

Saya mengalami kesulitan memahami versi TIDAK ADA. Apakah Anda tidak kehilangan agregasi di bagian subquery? Jika saya menjalankan ini di meja saya, saya hanya mendapatkan kembali 3 catatan karyawan dari 40 karyawan yang saya miliki di meja. Saya harus mendapatkan setidaknya 40 catatan. Dalam permintaan dalam, bukankah kita juga harus cocok dengan nama pengguna?
Narshe

Ini berfungsi untuk saya menggunakan yang berikut: SELECT username, value FROM t WHERE NOT EXISTS ( SELECT * FROM t AS witness WHERE witness.date > t.date AND witness.username = t.username );
Narshe

Saya telah melihat TIDAK ADA dan sepertinya hanya mengembalikan entri yang lebih tinggi untuk semua pengguna yang bertentangan dengan: "kueri yang akan memberi saya tanggal terbaru untuk setiap pengguna".
Tasos Zervos

Anda memang benar, saya memperbarui kueri saya. Terima kasih atas komentar anda! @ Narshe maaf saya ketinggalan komentar Anda karena beberapa alasan: / Tapi Anda benar sekali.
Fabian Pijcke

2

Yang ini harus memberi Anda hasil yang benar untuk pertanyaan Anda yang diedit.

Sub-permintaan memastikan untuk menemukan hanya baris dari tanggal terbaru, dan bagian luar GROUP BYakan mengurus ikatan. Ketika ada dua entri untuk tanggal yang sama untuk pengguna yang sama, itu akan mengembalikan entri dengan yang tertinggi 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

Anda juga bisa menggunakan Fungsi Peringkat analitis

    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

Akan mengatasi masalah yang diperbarui. Ini mungkin tidak bekerja dengan baik pada tabel besar, bahkan dengan pengindeksan yang baik.


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

0

Untuk Oracle mengurutkan hasil yang ditetapkan dalam urutan menurun dan mengambil catatan pertama, sehingga Anda akan mendapatkan catatan terbaru:

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       

Ini mungkin tidak akan berhasil jika beberapa pengguna memiliki pesanan pada tanggal yang sama; bagaimana jika brad dan bob sama-sama memesan pada 2 Januari?
AHiggins

Saya mengelompokkan berdasarkan nama pengguna sehingga akan berfungsi dan hasilnya akan seperti ini: Nilai nama pengguna Tanggal 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
Satu atau dua kalimat dalam implementasi atau penjelasan sangat membantu menciptakan jawaban yang berkualitas.

0

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

Kueri Batin akan mengembalikan tanggal terbaru untuk pengguna saat ini, Kueri luar akan menarik semua data sesuai dengan hasil kueri batin.


0

Saya menggunakan cara ini untuk mengambil catatan terakhir untuk setiap pengguna yang saya miliki di meja saya. Itu adalah permintaan untuk mendapatkan lokasi terakhir untuk salesman sesuai dengan waktu terakhir terdeteksi pada perangkat 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)

Selamat datang di StackOverflow dan terima kasih telah mencoba membantu. Jawaban khusus kode seperti milik Anda kurang dihargai jika dibandingkan dengan jawaban yang menjelaskan solusinya.
Yunnosch

Baca cara-jawaban ini untuk memberikan jawaban berkualitas.
thewaywewere

dan. itu tidak kembali ke MAX untuk setiap nama pengguna, hanya ke baris tunggal terbaru.
IrvineCAGuy

0

Kompilasi kecil saya

  • diri joinlebih baik daripada bersarangselect
  • tetapi group bytidak memberi Anda primary keyyang lebih disukaijoin
  • kunci ini dapat diberikan oleh partition bydalam hubungannya dengan first_value( docs )

Jadi, inilah pertanyaannya:

Pilih
 t. *
dari 
 Table t inner join (
  pilih first_value (ID) yang berbeda di atas (dipartisi berdasarkan GroupColumn dengan urutan DateColumn) sebagai ID
  dari Table
  di mana FilterColumn = 'nilai'
 ) j pada t.ID = j.ID

Pro:

  • Saring data dengan wherepernyataan menggunakan kolom apa saja
  • select setiap kolom dari baris yang difilter

Cons:

  • Perlu MS SQL Server dimulai dengan 2012.

0

Saya melakukan sedikit untuk aplikasi saya karena:

Di bawah ini adalah kueri:

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

Ini mirip dengan salah satu jawaban di atas, tetapi menurut saya itu jauh lebih sederhana dan lebih rapi. Juga, menunjukkan penggunaan yang baik untuk pernyataan silang berlaku. Untuk SQL Server 2005 dan di atasnya ...

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
Sementara kode ini dapat menyelesaikan pertanyaan, termasuk penjelasan tentang bagaimana dan mengapa ini menyelesaikan masalah akan sangat membantu untuk meningkatkan kualitas posting Anda, dan mungkin menghasilkan lebih banyak suara. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa depan, bukan hanya orang yang bertanya sekarang. Harap edit jawaban Anda untuk menambahkan penjelasan dan berikan indikasi tentang batasan dan asumsi apa yang berlaku. Dari Ulasan
double-beep

-2

Ini juga harus berfungsi untuk mendapatkan semua entri terbaru untuk pengguna.

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

1
Hai, kolom nilai harus berada di grup dengan klausa.
Juan Ruiz de Castilla

-4

Anda akan menggunakan fungsi agregat MAX dan GROUP BY

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

7
Hasil edit Anda hanya akan memilih secara acak value, bukan yang terkait dengan MAX(date)baris.
Alison R.

itu akan memberikan tanggal maksimal tetapi nama pengguna dan nilai mungkin tidak memiliki catatan yang sama.
SKR
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.