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.
sql
oracle
greatest-n-per-group
Umang
sumber
sumber
Jawaban:
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.
"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.
sumber
MAX(...) OVER (...)
Anda juga dapat menggunakanROW_NUMBER() OVER (...)
(untuk top-n-per-grup) atauRANK() OVER (...)
(untuk terbesar-n-per-grup).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.
Dengan kata lain: ambil baris dari
t1
tempat 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 memilikiauto_inc(seq)
kunci, misid
. Untuk menghindari penggandaan bisa digunakan berikut:Komentar ulang dari @ Farhan:
Berikut penjelasan yang lebih rinci:
Gabung luar mencoba bergabung
t1
dengant2
. Secara default, semua hasilt1
dikembalikan, dan jika ada kecocokan dit2
, itu juga dikembalikan. Jika tidak ada kecocokant2
untuk satu baris tertentut1
, maka kueri masih mengembalikan barist1
, dan digunakanNULL
sebagai pengganti untuk semuat2
kolom. Begitulah cara kerja gabungan luar secara umum.Trik dalam kueri ini adalah merancang kondisi pencocokan gabungan sedemikian rupa sehingga
t2
harus samauserid
, dan lebih besardate
. Gagasannya adalah jika ada barist2
yang memiliki lebih besardate
, maka baris dit1
dalamnya dibandingkan dengan tidak bisa menjadi yang terbaikdate
untuk ituuserid
. Tetapi jika tidak ada kecocokan - yaitu jika tidak ada barist2
dengan yang lebih besardate
dari pada barist1
- kita tahu bahwa baris dalamt1
adalah baris dengan baris terbesardate
untuk yang diberikanuserid
.Dalam kasus tersebut (ketika tidak ada kecocokan), kolom
t2
akan menjadiNULL
- bahkan kolom yang ditentukan dalam kondisi gabungan. Jadi itu sebabnya kami menggunakanWHERE t2.UserId IS NULL
, karena kami sedang mencari kasus di mana tidak ada baris ditemukan dengan yang lebih besardate
untuk yang diberikanuserid
.sumber
sumber
Saya tidak tahu nama kolom persis Anda, tetapi akan menjadi seperti ini:
sumber
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):
EDIT: Baru mencobanya:
Jadi itu berhasil, meskipun beberapa hal baru yang disebutkan di tempat lain mungkin lebih baik.
sumber
Saya tahu Anda meminta Oracle, tetapi dalam SQL 2005 kami sekarang menggunakan ini:
sumber
Saya tidak memiliki Oracle untuk mengujinya, tetapi solusi yang paling efisien adalah dengan menggunakan kueri analitik. Seharusnya terlihat seperti ini:
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 ...)
sumber
Bukankah klausa KUALIFIKASI akan menjadi yang paling sederhana dan terbaik?
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.
sumber
rank()
fungsi dalam situasi di mana ada ikatan. Anda bisa mendapatkan lebih dari saturank=1
. Lebih baik digunakanrow_number()
jika Anda benar-benar ingin hanya satu catatan yang dikembalikan.QUALIFY
klausa ini khusus untuk Teradata. Di Oracle (setidaknya) Anda harus membuat sarang permintaan dan memfilter menggunakanWHERE
klausa pada pernyataan pilih pembungkus (yang mungkin menyentuh kinerja sentuhan, saya bayangkan).Di
Oracle 12c+
, Anda bisa menggunakan n Top queries bersama dengan fungsi analitikrank
untuk mencapai ini dengan sangat ringkas tanpa subqueries:Di atas mengembalikan semua baris dengan max my_date per pengguna.
Jika Anda hanya menginginkan satu baris dengan tanggal maksimum, ganti
rank
denganrow_number
:sumber
Gunakan
ROW_NUMBER()
untuk menetapkan peringkat unik pada menurunDate
untuk masing-masingUserId
, lalu filter ke baris pertama untuk masing-masingUserId
(yaitu,ROW_NUMBER
= 1).sumber
Dengan PostgreSQL 8.4 atau lebih baru, Anda dapat menggunakan ini:
sumber
Saya pikir Anda harus membuat varian ini untuk permintaan sebelumnya:
sumber
sumber
Baru saja menulis contoh "langsung" di tempat kerja :)
Yang ini mendukung beberapa nilai untuk UserId pada tanggal yang sama .
Kolom: UserId, Nilai, Tanggal
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.
sumber
sumber
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 ...
sumber
(T-SQL) Pertama dapatkan semua pengguna dan maxdate mereka. Bergabunglah dengan tabel untuk menemukan nilai-nilai yang sesuai untuk pengguna di hari-hari terakhir.
hasil:
sumber
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)?
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.
Semoga ini bisa membantu (siapa saja) .. :)
Salam, Guus
sumber
Dengan asumsi Tanggal unik untuk UserID yang diberikan, inilah beberapa TSQL:
sumber
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.
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.
sumber
IMHO ini berfungsi. HTH
sumber
Saya pikir ini harus berhasil?
sumber
Coba pertama kali saya salah membaca pertanyaan, mengikuti jawaban teratas, berikut adalah contoh lengkap dengan hasil yang benar:
-
-
sumber
Ini juga akan menangani duplikat (kembalikan satu baris untuk setiap user_id):
sumber
Baru saja menguji ini dan tampaknya berfungsi pada tabel logging
sumber
Ini harus sesederhana:
sumber
Solusi untuk MySQL yang tidak memiliki konsep partisi KEEP, DENSE_RANK.
Referensi: http://benincampus.blogspot.com/2013/08/select-rows-which-have-maxmin-value-in.html
sumber
Jika Anda menggunakan Postgres, Anda dapat menggunakan
array_agg
likeSaya tidak terbiasa dengan Oracle. Inilah yang saya pikirkan
Kedua kueri mengembalikan hasil yang sama dengan jawaban yang diterima. Lihat SQLFiddles:
sumber
Jika (UserID, Tanggal) adalah unik, yaitu tidak ada tanggal yang muncul dua kali untuk pengguna yang sama, maka:
sumber
sumber