300k rækker er ikke et stort bord. Vi ser ofte 300 millioner rækketabeller.
Det største problem med din forespørgsel er, at du bruger en korreleret underforespørgsel, så den skal genudføre underforespørgslen for hver række i den ydre forespørgsel.
Det er ofte sådan, at du ikke behøver at gøre alt dit arbejde i én SQL-sætning. Der er fordele ved at dele det op i flere enklere SQL-sætninger:
- Nemmere at kode.
- Nemmere at optimere.
- Nemmere at fejlfinde.
- Lettere at læse.
- Nemmere at vedligeholde, hvis/når du skal implementere nye krav.
Antal køb
SELECT customer, COUNT(sale) AS number_of_purchases
FROM sales
GROUP BY customer;
Et indeks over salg (kunde, salg) ville være bedst til denne forespørgsel.
Sidste købsværdi
Dette er den største-n-per-gruppe problem, der dukker op ofte.
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 andre ord, prøv at matche række a
til en hypotetisk række b
der har den samme kunde og en større dato. Hvis der ikke findes en sådan række, så a
skal have den bedste dato for den pågældende kunde.
Et indeks over salg (kunde, datoer, udsalg) ville være bedst til denne forespørgsel.
Hvis du måske har mere end ét salg til en kunde på den største dato, returnerer denne forespørgsel mere end én række pr. kunde. Du bliver nødt til at finde en anden kolonne for at bryde uafgjort. Hvis du bruger en primær nøgle med auto-increment, er den velegnet som uafgjort, fordi den med garanti er unik, og den har tendens til at stige kronologisk.
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;
Samlet antal køb, når det har en positiv værdi
SELECT customer, SUM(sale) AS total_purchases
FROM sales
WHERE sale > 0
GROUP BY customer;
Et indeks over salg (kunde, salg) ville være bedst til denne forespørgsel.
Du bør overveje at bruge NULL til at angive en manglende salgsværdi i stedet for -1. Aggregerede funktioner som SUM() og COUNT() ignorerer NULL, så du behøver ikke bruge en WHERE-sætning for at udelukke rækker med salg <0.
Re:din kommentar
Top fem kunder for 4. kvartal 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;
Jeg vil gerne teste det mod rigtige data, men jeg tror, at et indeks for salg (år, kvartal, kunde, salg) ville være bedst til denne forespørgsel.
Sidste køb for kunder med samlede køb> 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 anden største-n-pr-gruppe forespørgsel ovenfor, ville et indeks for salg (kunde, datoer, udsalg) være bedst for denne forespørgsel. Det kan formentlig ikke optimere både join og gruppe ved, så dette vil medføre en midlertidig tabel. Men det vil i det mindste kun klare én midlertidig tabel i stedet for mange.
Disse forespørgsler er komplekse nok. Du bør ikke prøve at skrive en enkelt SQL-forespørgsel, der kan give alt af disse resultater. Husk det klassiske citat fra Brian Kernighan: