Ambil baris yang memiliki nilai Max untuk kolom

575

Meja:

UserId, Value, Date.

Saya ingin mendapatkan UserId, Nilai untuk maks (Tanggal) untuk setiap UserId. Artinya, Nilai untuk setiap UserID yang memiliki tanggal terbaru. Apakah ada cara untuk melakukan ini dalam SQL? (Lebih disukai Oracle)

Pembaruan: Permintaan maaf untuk ambiguitas apa pun: Saya harus mendapatkan SEMUA UserIds. Tetapi untuk setiap UserId, hanya baris itu di mana pengguna itu memiliki tanggal terbaru.

Umang
sumber
21
Bagaimana jika ada beberapa baris yang memiliki nilai tanggal maksimum untuk userid tertentu?
David Aldridge
Apa sajakah bidang kunci dari tabel?
vamosrafa
beberapa solusi di bawah ini dibandingkan: sqlfiddle.com/#!4/6d4e81/1
Used_By_Already
1
@ Davidvidridge, kolom itu mungkin unik.
Pacerier

Jawaban:

398

Ini akan mengambil semua baris yang nilai kolom my_date sama dengan nilai maksimum my_date untuk userid itu. Ini dapat mengambil beberapa baris untuk userid di mana tanggal maksimum pada beberapa baris.

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

"Fungsi analitik rock"

Edit: Sehubungan dengan komentar pertama ...

"menggunakan kueri analitik dan bergabung sendiri mengalahkan tujuan kueri analitik"

Tidak ada swa-gabung dalam kode ini. Sebaliknya ada predikat yang ditempatkan pada hasil tampilan inline yang berisi fungsi analitik - masalah yang sangat berbeda, dan praktik yang sepenuhnya standar.

"Jendela default di Oracle adalah dari baris pertama di partisi ke yang sekarang"

Klausa windowing hanya berlaku di hadapan pesanan dengan klausa. Tanpa urutan oleh klausa, tidak ada klausa windowing yang diterapkan secara default dan tidak ada yang dapat ditentukan secara eksplisit.

Kode berfungsi.

David Aldridge
sumber
39
Ketika diterapkan ke tabel yang memiliki 8,8 juta baris, kueri ini menghabiskan separuh waktu kueri dalam beberapa jawaban lain yang sangat dipilih.
Derek Mahar
4
Adakah yang mau memposting tautan ke MySQL yang setara dengan ini, jika ada?
Wangi
2
Tidak bisakah ini mengembalikan duplikat? Misalnya. jika dua baris memiliki user_id yang sama dan tanggal yang sama (yang merupakan maks).
jastr
2
@astr Saya pikir itu diakui dalam pertanyaan
David Aldridge
3
Alih-alih MAX(...) OVER (...)Anda juga dapat menggunakan ROW_NUMBER() OVER (...)(untuk top-n-per-grup) atau RANK() OVER (...)(untuk terbesar-n-per-grup).
MT0
441

Saya melihat banyak orang menggunakan subqueries atau fitur khusus vendor untuk melakukan ini, tetapi saya sering melakukan query seperti ini tanpa subqueries dengan cara berikut. Ia menggunakan SQL standar yang sederhana sehingga harus bekerja di merek RDBMS apa pun.

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;

Dengan kata lain: ambil baris dari t1tempat tidak ada baris lain yang samaUserId dan lebih besar.

(Saya menempatkan pengidentifikasi "Tanggal" di pembatas karena itu kata yang dilindungi undang-undang.)

Dalam hal jika t1."Date" = t2."Date" , penggandaan muncul. Biasanya tabel memiliki auto_inc(seq)kunci, mis id. Untuk menghindari penggandaan bisa digunakan berikut:

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;

Komentar ulang dari @ Farhan:

Berikut penjelasan yang lebih rinci:

Gabung luar mencoba bergabung t1dengan t2. Secara default, semua hasil t1dikembalikan, dan jika ada kecocokan di t2, itu juga dikembalikan. Jika tidak ada kecocokan t2untuk satu baris tertentut1 , maka kueri masih mengembalikan baris t1, dan digunakan NULLsebagai pengganti untuk semuat2 kolom. Begitulah cara kerja gabungan luar secara umum.

Trik dalam kueri ini adalah merancang kondisi pencocokan gabungan sedemikian rupa sehingga t2harus sama userid , dan lebih besar date . Gagasannya adalah jika ada baris t2yang memiliki lebih besar date, maka baris di t1dalamnya dibandingkan dengan tidak bisa menjadi yang terbaik dateuntuk itu userid. Tetapi jika tidak ada kecocokan - yaitu jika tidak ada baris t2dengan yang lebih besar datedari pada baris t1- kita tahu bahwa baris dalamt1 adalah baris dengan baris terbesar dateuntuk yang diberikan userid.

Dalam kasus tersebut (ketika tidak ada kecocokan), kolom t2akan menjadi NULL- bahkan kolom yang ditentukan dalam kondisi gabungan. Jadi itu sebabnya kami menggunakan WHERE t2.UserId IS NULL, karena kami sedang mencari kasus di mana tidak ada baris ditemukan dengan yang lebih besar dateuntuk yang diberikan userid.

Bill Karwin
sumber
7
Wow Bill. Ini adalah solusi paling kreatif untuk masalah ini yang pernah saya lihat. Ini juga sangat performant di set data saya yang cukup besar. Ini pasti mengalahkan banyak solusi lain yang pernah saya lihat atau usaha saya sendiri dalam memecahkan kesulitan ini.
Justin Noel
37
Ketika diterapkan ke tabel yang memiliki 8,8 juta baris, kueri ini memakan waktu hampir dua kali lipat dari yang ada di jawaban yang diterima.
Derek Mahar
16
@Derek: Optimalisasi bergantung pada merek dan versi RDBMS, serta keberadaan indeks, tipe data, dll yang sesuai
Bill Karwin
7
Di MySQL, permintaan semacam ini tampaknya menyebabkannya mengulang hasil gabungan Cartesian di antara tabel, menghasilkan waktu O (n ^ 2). Menggunakan metode subquery bukannya mengurangi waktu kueri dari 2.0s menjadi 0.003s. YMMV.
Jesse
1
Apakah ada cara untuk mengadaptasi ini agar cocok dengan baris di mana tanggal adalah tanggal terbesar kurang dari atau sama dengan tanggal yang diberikan pengguna? Misalnya jika pengguna memberikan tanggal "23-OCT-2011", dan tabel termasuk baris untuk "24-OCT-2011", "22-OCT-2011", "20-OCT-2011", maka saya ingin dapatkan "22-OCT-2011". Telah menggaruk-garuk kepalaku dan membaca cuplikan ini untuk sementara waktu sekarang ...
Cory Kendall
164
SELECT userid, MAX(value) KEEP (DENSE_RANK FIRST ORDER BY date DESC)
  FROM table
  GROUP BY userid
Dave Costa
sumber
3
Dalam pengujian saya menggunakan tabel yang memiliki banyak baris, solusi ini memakan waktu sekitar dua kali lebih lama dari pada jawaban yang diterima.
Derek Mahar
7
Tolong tunjukkan tes Anda
Rob van Wijk
Saya mengkonfirmasi ini jauh lebih cepat daripada solusi lain
tamersalama
5
masalahnya adalah tidak mengembalikan catatan lengkap
Used_By_Already
@ user2067753 Tidak, itu tidak mengembalikan catatan lengkap. Anda dapat menggunakan ekspresi MAX () .. KEEP .. yang sama pada beberapa kolom, sehingga Anda dapat memilih semua kolom yang Anda butuhkan. Tapi itu tidak nyaman jika Anda ingin sejumlah besar kolom dan lebih suka menggunakan SELECT *.
Dave Costa
51

Saya tidak tahu nama kolom persis Anda, tetapi akan menjadi seperti ini:

    pilih userid, nilai
      dari pengguna u1
     di mana date = (pilih maks (tanggal)
                     dari pengguna u2
                    di mana u1.userid = u2.userid)
Steve K.
sumber
3
Mungkin tidak terlalu efisien, Steve.
David Aldridge
7
Anda mungkin meremehkan pengoptimal permintaan Oracle.
Rafał Dowgird
3
Tidak semuanya. Ini hampir pasti akan diterapkan sebagai pemindaian penuh dengan loop bersarang bergabung untuk mendapatkan tanggal. Anda sedang berbicara tentang io logis dalam urutan 4 kali jumlah baris dalam tabel dan mengerikan untuk jumlah data yang tidak sepele.
David Aldridge
4
FYI, "Tidak efisien, tetapi bekerja" sama dengan "Bekerja, tetapi tidak efisien". Kapan kita menyerah dengan efisien sebagai tujuan desain?
David Aldridge
6
+1 karena saat datatables Anda bukan jutaan baris panjangnya, ini adalah solusi yang paling mudah dipahami. ketika Anda memiliki banyak pengembang dari semua tingkat keterampilan memodifikasi kode, dapat dimengerti lebih penting daripada sepersekian detik dalam kinerja yang tidak terlihat.
n00b
35

Tidak sedang bekerja, saya tidak memiliki Oracle untuk ditangani, tetapi saya ingat bahwa Oracle memungkinkan beberapa kolom untuk dicocokkan dalam klausa IN, yang setidaknya harus menghindari opsi yang menggunakan subquery yang dikorelasikan, yang jarang bagus. ide.

Sesuatu seperti ini, mungkin (tidak ingat apakah daftar kolom harus ditulis dalam tanda kurung atau tidak):

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

EDIT: Baru mencobanya:

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

Jadi itu berhasil, meskipun beberapa hal baru yang disebutkan di tempat lain mungkin lebih baik.

Mike Woodhouse
sumber
4
Ini berfungsi dengan baik di PostgreSQL juga. Dan saya suka kesederhanaan dan generalitasnya - subquery mengatakan "Inilah kriteria saya", permintaan luar mengatakan "Dan inilah detail yang ingin saya lihat". +1.
j_random_hacker
13

Saya tahu Anda meminta Oracle, tetapi dalam SQL 2005 kami sekarang menggunakan ini:


-- 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
mancaus
sumber
7

Saya tidak memiliki Oracle untuk mengujinya, tetapi solusi yang paling efisien adalah dengan menggunakan kueri analitik. Seharusnya terlihat seperti ini:

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

Saya menduga bahwa Anda dapat menyingkirkan permintaan luar dan menempatkan berbeda di bagian dalam, tapi saya tidak yakin. Sementara itu saya tahu ini bekerja.

Jika Anda ingin belajar tentang pertanyaan analitik, saya sarankan membaca http://www.orafaq.com/node/55 dan http://www.akadia.com/services/ora_analytic_functions.html . Berikut ini ringkasan singkatnya.

Di bawah tudung kueri analitik, urutkan seluruh dataset, lalu proses secara berurutan. Saat Anda memprosesnya, Anda mempartisi dataset sesuai dengan kriteria tertentu, dan kemudian untuk setiap baris terlihat pada beberapa jendela (default ke nilai pertama di partisi ke baris saat ini - default itu juga yang paling efisien) dan dapat menghitung nilai menggunakan jumlah fungsi analitik (daftar yang sangat mirip dengan fungsi agregat).

Dalam hal ini di sini adalah apa yang dilakukan permintaan dalam. Seluruh dataset diurutkan berdasarkan UserID lalu Date DESC. Kemudian memprosesnya dalam satu pass. Untuk setiap baris Anda mengembalikan UserId dan Tanggal pertama yang terlihat untuk UserId itu (karena tanggal diurutkan DESC, itulah tanggal maks). Ini memberi Anda jawaban Anda dengan baris yang digandakan. Kemudian luar DISTINCT squashes duplikat.

Ini bukan contoh yang sangat spektakuler dari pertanyaan analitik. Untuk kemenangan yang jauh lebih besar, pertimbangkan untuk mengambil tabel penerimaan keuangan dan menghitung untuk setiap pengguna dan tanda terima, jumlah total yang mereka bayar. Kueri analitik menyelesaikannya dengan efisien. Solusi lain kurang efisien. Itulah sebabnya mereka adalah bagian dari standar SQL 2003. (Sayangnya Postgres belum memilikinya. Grrr ...)

pengguna11318
sumber
Anda juga harus mengembalikan nilai tanggal untuk menjawab pertanyaan sepenuhnya. Jika itu berarti klausa first_value lain maka saya akan menyarankan bahwa solusinya lebih kompleks daripada seharusnya, dan metode analitik berdasarkan max (tanggal) berbunyi lebih baik.
David Aldridge
Pernyataan pertanyaan tidak mengatakan apa pun tentang mengembalikan tanggal. Anda dapat melakukannya dengan menambahkan FIRST (Tanggal) lain atau hanya dengan menanyakan Tanggal dan mengubah permintaan luar ke GROUP BY. Saya akan menggunakan yang pertama dan mengharapkan optimizer untuk menghitung keduanya dalam satu pass.
user11318
"Pernyataan pertanyaan tidak mengatakan apa pun tentang mengembalikan tanggal" ... ya, Anda benar. Maaf. Tetapi menambahkan lebih banyak klausa FIRST_VALUE akan menjadi berantakan dengan cepat. Ini semacam jendela tunggal, tetapi jika Anda memiliki 20 kolom untuk kembali untuk baris itu maka Anda telah menulis banyak kode untuk mengarungi.
David Aldridge
Itu juga terjadi pada saya bahwa solusi ini adalah non-deterministik untuk data di mana userid tunggal memiliki beberapa baris yang memiliki tanggal maksimum dan VALUE berbeda. Lebih banyak kesalahan dalam pertanyaan daripada jawaban.
David Aldridge
1
Saya setuju itu menyakitkan verbose. Namun bukankah itu umumnya terjadi dengan SQL? Dan Anda benar bahwa solusinya adalah non-deterministik. Ada banyak cara untuk menangani ikatan, dan terkadang masing-masing adalah yang Anda inginkan.
user11318
6

Bukankah klausa KUALIFIKASI akan menjadi yang paling sederhana dan terbaik?

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

Untuk konteks, pada Teradata di sini tes ukuran yang layak ini berjalan di 17-an dengan versi KUALIFIKASI ini dan di 23-an dengan 'inline view' / solusi Aldridge # 1.

wcw
sumber
1
Ini jawaban terbaik menurut saya. Namun, berhati-hatilah dengan rank()fungsi dalam situasi di mana ada ikatan. Anda bisa mendapatkan lebih dari satu rank=1. Lebih baik digunakan row_number()jika Anda benar-benar ingin hanya satu catatan yang dikembalikan.
cartbeforehorse
1
Perlu diketahui juga bahwa QUALIFYklausa ini khusus untuk Teradata. Di Oracle (setidaknya) Anda harus membuat sarang permintaan dan memfilter menggunakan WHEREklausa pada pernyataan pilih pembungkus (yang mungkin menyentuh kinerja sentuhan, saya bayangkan).
cartbeforehorse
5

Di Oracle 12c+, Anda bisa menggunakan n Top queries bersama dengan fungsi analitik rankuntuk mencapai ini dengan sangat ringkas tanpa subqueries:

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

Di atas mengembalikan semua baris dengan max my_date per pengguna.

Jika Anda hanya menginginkan satu baris dengan tanggal maksimum, ganti rankdengan 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; 
Gurwinder Singh
sumber
5

Gunakan ROW_NUMBER()untuk menetapkan peringkat unik pada menurun Dateuntuk masing-masing UserId, lalu filter ke baris pertama untuk masing-masing UserId(yaitu, 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;
markusk
sumber
5

Dengan PostgreSQL 8.4 atau lebih baru, Anda dapat menggunakan ini:

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
Cito
sumber
3

Saya pikir Anda harus membuat varian ini untuk permintaan sebelumnya:

SELECT UserId, Value FROM Users U1 WHERE 
Date = ( SELECT MAX(Date)    FROM Users where UserId = U1.UserId)
stefano m
sumber
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  
Aheho
sumber
3

Baru saja menulis contoh "langsung" di tempat kerja :)

Yang ini mendukung beberapa nilai untuk UserId pada tanggal yang sama .

Kolom: UserId, Nilai, Tanggal

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
)

Anda dapat menggunakan FIRST_VALUE alih-alih MAX dan mencarinya di paket penjelasan. Saya tidak punya waktu untuk bermain dengannya.

Tentu saja, jika mencari melalui tabel besar, mungkin lebih baik jika Anda menggunakan petunjuk FULL dalam permintaan Anda.

Truper
sumber
3
select VALUE from TABLE1 where TIME = 
   (select max(TIME) from TABLE1 where DATE= 
   (select max(DATE) from TABLE1 where CRITERIA=CRITERIA))
Nouky
sumber
2

Saya pikir sesuatu seperti ini. (Maafkan saya atas kesalahan sintaksis apa pun; saya sudah terbiasa menggunakan HQL saat ini!)

EDIT: Juga salah membaca pertanyaan! Memperbaiki kueri ...

SELECT UserId, Value
FROM Users AS user
WHERE Date = (
    SELECT MAX(Date)
    FROM Users AS maxtest
    WHERE maxtest.UserId = user.UserId
)
jdmichal
sumber
Tidak memenuhi ketentuan "untuk setiap UserId"
David Aldridge
Di mana itu akan gagal? Untuk setiap UserID di Pengguna, akan dijamin bahwa setidaknya satu baris berisi UserID akan dikembalikan. Atau apakah saya melewatkan kasus khusus di suatu tempat?
jdmichal
2

(T-SQL) Pertama dapatkan semua pengguna dan maxdate mereka. Bergabunglah dengan tabel untuk menemukan nilai-nilai yang sesuai untuk pengguna di hari-hari terakhir.

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

hasil:

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

Jawabannya di sini adalah hanya Oracle. Inilah jawaban yang sedikit lebih canggih di semua SQL:

Siapa yang memiliki hasil pekerjaan rumah keseluruhan terbaik (jumlah maksimum poin pekerjaan rumah)?

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)

Dan contoh yang lebih sulit, yang perlu penjelasan, yang mana saya tidak punya waktu:

Berikan buku (ISBN dan judul) yang paling populer di tahun 2008, yaitu yang paling sering dipinjam di tahun 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);

Semoga ini bisa membantu (siapa saja) .. :)

Salam, Guus

Guus
sumber
Jawaban yang diterima bukan "Oracle saja" - melainkan SQL standar (didukung oleh banyak DBMS)
a_horse_with_no_name
2

Dengan asumsi Tanggal unik untuk UserID yang diberikan, inilah beberapa 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 
marc
sumber
2

Saya cukup terlambat ke pesta, tetapi peretasan berikut akan mengungguli subquery yang berkorelasi dan fungsi analitik apa pun tetapi memiliki satu batasan: nilai harus dikonversi ke string. Jadi itu berfungsi untuk tanggal, angka dan string lainnya. Kode tidak terlihat bagus tetapi profil pelaksanaannya bagus.

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

Alasan mengapa kode ini bekerja dengan sangat baik adalah hanya perlu memindai tabel satu kali. Itu tidak memerlukan indeks apa pun dan yang paling penting itu tidak perlu mengurutkan tabel, yang sebagian besar fungsi analitik lakukan. Indeks akan membantu sekalipun jika Anda perlu memfilter hasil untuk satu userid.

aLevelOfIndirection
sumber
Ini adalah rencana pelaksanaan yang baik dibandingkan dengan kebanyakan, tetapi menerapkan semua trik itu untuk lebih dari beberapa bidang akan membosankan dan mungkin berhasil melawannya. Tapi sangat menarik - terima kasih. lihat sqlfiddle.com/#!4/2749b5/23
Used_By_Already
Anda benar itu bisa menjadi membosankan, itulah sebabnya ini harus dilakukan hanya ketika kinerja permintaan membutuhkannya. Seperti itu sering terjadi dengan skrip ETL.
aLevelOfIndirection
ini sangat bagus. melakukan sesuatu yang mirip menggunakan LISTAGG tetapi terlihat jelek. postgres memiliki altenative yang lebih baik menggunakan array_agg. lihat jawaban saya :)
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 ini berfungsi. HTH

Zsolt Botykai
sumber
1

Saya pikir ini harus berhasil?

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
GateKiller
sumber
1

Coba pertama kali saya salah membaca pertanyaan, mengikuti jawaban teratas, berikut adalah contoh lengkap dengan hasil yang benar:

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)
KyleLanser
sumber
1

Ini juga akan menangani duplikat (kembalikan satu baris untuk setiap 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
na43251
sumber
1

Baru saja menguji ini dan tampaknya berfungsi pada tabel logging

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

Ini harus sesederhana:

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

Solusi untuk MySQL yang tidak memiliki konsep partisi 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

Referensi: http://benincampus.blogspot.com/2013/08/select-rows-which-have-maxmin-value-in.html

Ben Lin
sumber
Ini tidak bekerja " pada DB lain juga ". Ini hanya berfungsi pada MySQL dan mungkin pada SQL Server karena memiliki konsep variabel yang serupa. Ini pasti tidak akan berfungsi pada Oracle, Postgres, DB2, Derby, H2, HSQLDB, Vertica, Greenplum. Selain itu jawaban yang diterima adalah standar ANSI SQL (yang hanya tahu MySQL tidak mendukung)
a_horse_with_no_name
kuda, kurasa kamu benar. Saya tidak memiliki pengetahuan tentang DB lain, atau ANSI. Solusi saya dapat menyelesaikan masalah di MySQL, yang tidak memiliki dukungan yang tepat untuk ANSI SQL untuk menyelesaikannya dengan cara standar.
Ben Lin
1

Jika Anda menggunakan Postgres, Anda dapat menggunakan array_agglike

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

Saya tidak terbiasa dengan Oracle. Inilah yang saya pikirkan

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 

Kedua kueri mengembalikan hasil yang sama dengan jawaban yang diterima. Lihat SQLFiddles:

  1. Jawaban yang diterima
  2. Solusi saya dengan Postgres
  3. Solusi saya dengan Oracle
Bruno Calza
sumber
0

Jika (UserID, Tanggal) adalah unik, yaitu tidak ada tanggal yang muncul dua kali untuk pengguna yang sama, maka:

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;
menemukan
sumber
Saya percaya bahwa Anda perlu bergabung dengan UserID juga
Tom H
0
select   UserId,max(Date) over (partition by UserId) value from users;
Amitābha
sumber
2
Ini akan mengembalikan semua baris, bukan hanya satu baris per pengguna.
Jon Heller