300k baris bukanlah meja besar. Kita sering melihat 300 juta tabel baris.
Masalah terbesar dengan kueri Anda adalah Anda menggunakan subkueri yang berkorelasi, sehingga subkueri tersebut harus dijalankan kembali untuk setiap baris di kueri luar.
Sering kali Anda tidak perlu melakukan semua pekerjaan Anda dalam satu pernyataan SQL. Ada keuntungan untuk memecahnya menjadi beberapa pernyataan SQL yang lebih sederhana:
- Lebih mudah dikodekan.
- Lebih mudah untuk dioptimalkan.
- Lebih mudah untuk di-debug.
- Lebih mudah dibaca.
- Lebih mudah dirawat jika/ketika Anda harus menerapkan persyaratan baru.
Jumlah Pembelian
SELECT customer, COUNT(sale) AS number_of_purchases
FROM sales
GROUP BY customer;
Indeks penjualan (pelanggan, penjualan) akan menjadi yang terbaik untuk kueri ini.
Nilai Pembelian Terakhir
Ini adalah terbesar-n-per-grup masalah yang sering muncul.
SELECT a.customer, a.sale as max_sale
FROM sales a
LEFT OUTER JOIN sales b
ON a.customer=b.customer AND a.dates < b.dates
WHERE b.customer IS NULL;
Dengan kata lain, coba cocokkan baris a
ke baris hipotetis b
yang memiliki pelanggan yang sama dan tanggal yang lebih besar. Jika baris tersebut tidak ditemukan, maka a
harus memiliki tanggal terbaik untuk pelanggan itu.
Indeks penjualan (pelanggan, tanggal, penjualan) akan menjadi yang terbaik untuk kueri ini.
Jika Anda mungkin memiliki lebih dari satu penjualan untuk pelanggan pada tanggal terbesar tersebut, kueri ini akan mengembalikan lebih dari satu baris per pelanggan. Anda harus menemukan kolom lain untuk memutuskan ikatan. Jika Anda menggunakan kunci utama auto-increment, cocok sebagai tie breaker karena dijamin unik dan cenderung meningkat secara kronologis.
SELECT a.customer, a.sale as max_sale
FROM sales a
LEFT OUTER JOIN sales b
ON a.customer=b.customer AND (a.dates < b.dates OR a.dates = b.dates and a.id < b.id)
WHERE b.customer IS NULL;
Jumlah Total Pembelian, Bila Memiliki Nilai Positif
SELECT customer, SUM(sale) AS total_purchases
FROM sales
WHERE sale > 0
GROUP BY customer;
Indeks penjualan (pelanggan, penjualan) akan menjadi yang terbaik untuk kueri ini.
Anda harus mempertimbangkan untuk menggunakan NULL untuk menandakan nilai penjualan yang hilang alih-alih -1. Fungsi gabungan seperti SUM() dan COUNT() mengabaikan NULL, jadi Anda tidak perlu menggunakan klausa WHERE untuk mengecualikan baris dengan penjualan <0.
Re:komentar Anda
Lima Pelanggan Teratas untuk Q4 2012
SELECT customer, SUM(sale) AS total_purchases
FROM sales
WHERE (year, quarter) = (2012, 4) AND sale > 0
GROUP BY customer
ORDER BY total_purchases DESC
LIMIT 5;
Saya ingin mengujinya dengan data nyata, tetapi saya yakin indeks penjualan (tahun, kuartal, pelanggan, penjualan) akan menjadi yang terbaik untuk kueri ini.
Pembelian Terakhir untuk Pelanggan dengan Total Pembelian> 5
SELECT a.customer, a.sale as max_sale
FROM sales a
INNER JOIN sales c ON a.customer=c.customer
LEFT OUTER JOIN sales b
ON a.customer=b.customer AND (a.dates < b.dates OR a.dates = b.dates and a.id < b.id)
WHERE b.customer IS NULL
GROUP BY a.id
HAVING COUNT(*) > 5;
Seperti dalam kueri n-per-grup terbesar lainnya di atas, indeks penjualan (pelanggan, tanggal, penjualan) akan menjadi yang terbaik untuk kueri ini. Mungkin tidak dapat mengoptimalkan gabungan dan grup, jadi ini akan menimbulkan tabel sementara. Tapi setidaknya itu hanya akan melakukan satu tabel sementara, bukan banyak.
Kueri ini cukup kompleks. Anda tidak boleh mencoba menulis satu kueri SQL yang dapat memberikan semua dari hasil ini. Ingat kutipan klasik dari Brian Kernighan: