Reputation: 357
I'm trying to get a 30 day view of transaction totals as a graph. Currently using a VIEW for dates and LEFT OUTER JOIN to get data. This works, showing my SUM of all my transactions, and still including a 0 for days without transactions, etc.
The problem is that the transactions table is quite large. So that LEFT OUTER JOIN is doing a lot of work... and is slow. Here is the original query:
SELECT IFNULL(SUM(Total), 0) AS OrderTotal, MONTH(dates.date) AS Month, YEAR(dates.date) AS Year, DAY(dates.date) AS Day
FROM dates LEFT OUTER JOIN tblTransactions
ON DATE(tblTransactions.TransDate) = dates.date
WHERE dates.date > DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY DAY(dates.date), MONTH(dates.date)
ORDER BY dates.date
LIMIT 30
To speed things up, I thought it would be good to add an additional WHERE condition and only look at transactions older than 30 days. That sped things up incredibly, and thought I dialed it, but the problem is that it's only good if I have transactions for all 30 days. If one day is $0, it would return 29 records with this conditional. Not good.
SELECT IFNULL(SUM(Total), 0) AS OrderTotal, MONTH(dates.date) AS Month, YEAR(dates.date) AS Year, DAY(dates.date) AS Day
FROM dates LEFT OUTER JOIN tblTransactions
ON DATE(tblTransactions.TransDate) = dates.date
WHERE dates.date > DATE_SUB(NOW(), INTERVAL 30 DAY)
AND tblTransactions.TransDate > DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY DAY(dates.date), MONTH(dates.date)
ORDER BY dates.date
LIMIT 30
So was hoping for some feedback to improve the original query. Right now, I have a feeling a PHP for() loop - looping 30 times and querying 30 times might be faster than my original LEFT OUTER JOIN single query.
Upvotes: 1
Views: 358
Reputation: 1269445
The filtering on the second table in a left join
needs to go in the on
clause:
SELECT COALESCE(SUM(t.Total), 0) AS OrderTotal,
MONTH(d.date) AS Month, YEAR(dates.date) AS Year, DAY(d.date) AS Day
FROM dates d LEFT OUTER JOIN
tblTransactions t
ON DATE(t.TransDate) = d.date AND
t.TransDate > DATE_SUB(NOW(), INTERVAL 30 DAY)
WHERE d.date > DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY DAY(d.date), MONTH(d.date)
ORDER BY d.date
LIMIT 30;
Note: A cause for bad performance is likely this clause:
DATE(t.TransDate) = d.date
If TransDate
has no time component then this is not necessary. If performance on this type of query is important, add a computed column for just the date component of TransDate
and add an index on that column.
Upvotes: 2