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.

kepala ikan
sumber
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
Kemungkinan rangkap dari Cara mendapatkan catatan terakhir per grup dalam SQL
Patrick Honorez

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
RedFilter
sumber
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
dotjoe
sumber
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;
sujeet
sumber
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
)
Alison R.
sumber
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
)
Manix
sumber
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
Fabian Pijcke
sumber
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
Peter Lang
sumber
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
imba22
sumber
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.

Philip Kelley
sumber
0
SELECT *
FROM ReportStatus c
inner join ( SELECT 
  MAX(Date) AS MaxDate
  FROM ReportStatus ) m
on  c.date = m.maxdate
Narmadha
sumber
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
pengguna2014518
sumber
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       
wara
sumber
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
David
sumber
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.

Dheeraj Kumar
sumber
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
Mahmoud Hawa
sumber
0
SELECT * FROM TABEL1 WHERE DATE= (SELECT MAX(CREATED_DATE) FROM TABEL1)
AJAY
sumber
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.
resnyanskiy
sumber
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));    
Sajee
sumber
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
James Moore
sumber
0
SELECT MAX(DATE) AS dates 
FROM assignment  
JOIN paper_submission_detail ON  assignment.PAPER_SUB_ID = 
     paper_submission_detail.PAPER_SUB_ID 
bindra ashish
sumber
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
Vipin Kohli
sumber
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
Matthew Jones
sumber
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