300k rader är inget stort bord. Vi ser ofta 300 miljoner radtabeller.
Det största problemet med din fråga är att du använder en korrelerad underfråga, så den måste köra om underfrågan för varje rad i den yttre frågan.
Det är ofta så att du inte behöver göra allt ditt arbete i en SQL-sats. Det finns fördelar med att dela upp det i flera enklare SQL-satser:
- Lättare att koda.
- Lättare att optimera.
- Lättare att felsöka.
- Lättare att läsa.
- Lättare att underhålla om/när du måste implementera nya krav.
Antal köp
SELECT customer, COUNT(sale) AS number_of_purchases
FROM sales
GROUP BY customer;
Ett index över försäljning (kund, försäljning) skulle vara bäst för den här frågan.
Sista köpvärde
Det här är den största-n-per-gruppen problem som dyker upp ofta.
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;
Med andra ord, försök att matcha rad a
till en hypotetisk rad b
som har samma kund och ett större datum. Om ingen sådan rad hittas, a
måste ha det bästa datumet för den kunden.
Ett index över försäljning (kund, datum, rea) skulle vara bäst för den här frågan.
Om du kanske har mer än en rea för en kund på det största datumet kommer denna fråga att returnera mer än en rad per kund. Du måste hitta en annan kolumn för att bryta bandet. Om du använder en primärnyckel för automatisk ökning, är den lämplig som oavgjort, eftersom den garanterat är unik och den tenderar att öka kronologiskt.
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;
Totalt belopp för köp, när det har ett positivt värde
SELECT customer, SUM(sale) AS total_purchases
FROM sales
WHERE sale > 0
GROUP BY customer;
Ett index över försäljning (kund, försäljning) skulle vara bäst för den här frågan.
Du bör överväga att använda NULL för att ange ett saknat försäljningsvärde istället för -1. Aggregatfunktioner som SUM() och COUNT() ignorerar NULLs, så du behöver inte använda en WHERE-sats för att exkludera rader med rea <0.
Angående din kommentar
De fem bästa kunderna för fjärde kvartalet 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;
Jag skulle vilja testa det mot verkliga data, men jag tror att ett index på försäljning (år, kvartal, kund, försäljning) skulle vara bäst för den här frågan.
Sista köp för kunder med totala köp> 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;
Som i den andra största-n-per-grupp-frågan ovan, skulle ett index på försäljning (kund, datum, försäljning) vara bäst för den här frågan. Det kan antagligen inte optimera både kopplingen och gruppen, så detta kommer att medföra en tillfällig tabell. Men det kommer åtminstone bara göra ett tillfälligt bord istället för många.
Dessa frågor är tillräckligt komplexa. Du bör inte försöka skriva en enda SQL-fråga som kan ge allt av dessa resultat. Kom ihåg det klassiska citatet från Brian Kernighan: