Misalkan saya memiliki tabel pelanggan dan tabel pembelian. Setiap pembelian milik satu pelanggan. Saya ingin mendapatkan daftar semua pelanggan beserta pembelian terakhir mereka dalam satu pernyataan SELECT. Apa praktik terbaik? Adakah saran untuk membuat indeks?
Silakan gunakan nama tabel / kolom ini dalam jawaban Anda:
- pelanggan: id, nama
- pembelian: id, customer_id, item_id, tanggal
Dan dalam situasi yang lebih rumit, apakah akan (menguntungkan kinerja) bermanfaat untuk mendenormalkan basis data dengan menempatkan pembelian terakhir ke dalam tabel pelanggan?
Jika id (pembelian) dijamin disortir berdasarkan tanggal, dapatkah laporan disederhanakan dengan menggunakan sesuatu seperti LIMIT 1
?
Jawaban:
Ini adalah contoh dari
greatest-n-per-group
masalah yang telah muncul secara teratur di StackOverflow.Inilah cara saya biasanya merekomendasikan untuk menyelesaikannya:
Penjelasan: diberikan satu baris
p1
, seharusnya tidak ada barisp2
dengan pelanggan yang sama dan tanggal kemudian (atau dalam kasus ikatan, nantiid
). Ketika kami menemukan itu benar, makap1
adalah pembelian terbaru untuk pelanggan itu.Mengenai indeks, saya akan membuat indeks senyawa dalam
purchase
atas kolom (customer_id
,date
,id
). Itu memungkinkan sambungan luar dilakukan menggunakan indeks penutup. Pastikan untuk menguji pada platform Anda, karena optimasi bergantung pada implementasi. Gunakan fitur RDBMS Anda untuk menganalisis rencana pengoptimalan. MisalnyaEXPLAIN
di MySQL.Beberapa orang menggunakan subquery alih-alih solusi yang saya tunjukkan di atas, tetapi saya menemukan solusi saya membuatnya lebih mudah untuk menyelesaikan ikatan.
sumber
Anda juga dapat mencoba melakukan ini menggunakan sub pilih
Pilih harus bergabung pada semua pelanggan dan tanggal pembelian terakhir mereka .
sumber
INNER JOIN
ke aLEFT OUTER JOIN
.purchase
tabel adalah tanggal dan customer_id, tetapi permintaan meminta semua bidang dari tabel.Anda belum menentukan basis datanya. Jika itu adalah salah satu yang memungkinkan fungsi analitis mungkin lebih cepat untuk menggunakan pendekatan ini daripada GROUP BY satu (pasti lebih cepat di Oracle, kemungkinan besar lebih cepat di edisi SQL Server akhir, tidak tahu tentang yang lain).
Sintaks dalam SQL Server adalah:
sumber
Pendekatan lain adalah dengan menggunakan
NOT EXISTS
kondisi dalam kondisi bergabung Anda untuk menguji pembelian selanjutnya:sumber
AND NOT EXISTS
bagian itu dengan kata-kata yang mudah?Saya menemukan utas ini sebagai solusi untuk masalah saya.
Tetapi ketika saya mencobanya mereka kinerjanya rendah. Di bawah ini adalah saran saya untuk kinerja yang lebih baik.
Semoga ini bisa membantu.
sumber
top 1
danordered it by
MaxDatedesc
Jika Anda menggunakan PostgreSQL, Anda dapat menggunakan
DISTINCT ON
untuk menemukan baris pertama dalam sebuah grup.PostgreSQL Documents - Distinct On
Perhatikan bahwa
DISTINCT ON
bidang - di sinicustomer_id
- harus cocok dengan bidang paling kiri diORDER BY
klausa.Peringatan: Ini adalah klausa yang tidak standar.
sumber
Coba ini, ini akan membantu.
Saya telah menggunakan ini dalam proyek saya.
sumber
Diuji pada SQLite:
Fungsi
max()
agregat akan memastikan bahwa pembelian terakhir dipilih dari masing-masing kelompok (tetapi mengasumsikan bahwa kolom tanggal dalam format di mana max () memberikan yang terbaru - yang biasanya merupakan kasus). Jika Anda ingin menangani pembelian dengan tanggal yang sama maka Anda dapat menggunakannyamax(p.date, p.id)
.Dalam hal indeks, saya akan menggunakan indeks pada pembelian dengan (customer_id, tanggal, [kolom pembelian lainnya yang ingin Anda kembalikan di pilih Anda]).
The
LEFT OUTER JOIN
(sebagai lawanINNER JOIN
) akan memastikan bahwa pelanggan yang tidak pernah melakukan pembelian juga disertakan.sumber
Silakan coba ini,
sumber