tags:

views:

69

answers:

3

Running an EXPLAIN on some of my query tests have resulted in slow ALL joins even with indexes.
How do I make a MYSQL query with the following information more efficient?

Tables

counter: id (pk), timestamp, user_id (fk)
user: id (PK), username, website_id (fk)
website: id (pk), sitename

SELECT t2.username, t3.sitename, count(*) as views FROM counter t1
LEFT JOIN user t2 ON t2.id = t1.user_id
LEFT JOIN website t3 ON t3.id = t2.website_id
WHERE t1.id <> ""
GROUP BY t1.id
ORDER BY t1.id DESC 

The result in an html table:

username, sitename, counter_views

+2  A: 

Don't count(*), instead use count(t1.id)

Question Edit Change

With the question edit, you should put a column name in the count statement of what you want to count, e.g. count(user.id)

I believe the sql is wrong. Don't you want this:

select u.username, s.sitename, count(c.id)
from user u
join website s on u.website_id = s.id
join counter c on u.id = c.user_id
where u.id <> ""
group by u.username, s.sitename
order by u.id desc
Hogan
Yep, in a join you need to specify.
Tor Valamo
Ultimately, I'd like to use ORDER BY views [ count(c.id) as views ], but EXPLAIN gives ALL join, NULL key, using temporary, using filesort. Is there a better way?
rrrfusco
@rrrfusco what indexes have you set up?
Hogan
Each primary key and counter.user_id
rrrfusco
+1  A: 

Is your data populated yet? If these tables are empty, or very small, the optimizer may be choosing an 'all' query because the whole table is on one page. One page load from disk to get the whole table is faster than hitting a page on disk for the index, and then another page for the real data page.

TheDon
The data is relatively small, the counter 6000, users 100, sites 10.
rrrfusco
The counter table is probably on two pages, but even so, loading the whole table (2 pages) into memory has an equal cost in page hits to the minimum index lookup. So I'm not surprised by the 'all' joins. Does the behavior persist even if you bulk up the size of the tables, say 100 times?
TheDon
A: 

Your query looks wrong. I would use something like this:

SELECT u.username, w.sitename, c.views
FROM (
  SELECT user_id, COUNT(*) AS views FROM counter GROUP BY user_id
) AS c
LEFT JOIN user u ON u.id = c.user_id
LEFT JOIN website w ON w.id = u.website_id
ORDER BY c.views DESC

I would add index for counter.user_id too.

Michas
There is an index on user_id. I've always had problems with subselects. phpmyadmin is telling me c.user_id is an unknown column. ugh.. this will take some time...
rrrfusco
This query is wrong, if user has more than one website (as the design implies) then this will not work. If there is a one-to-one relationship in user website then there is a faster query that uses a join instead of a sub-query. (Sub-querys are often very very slow unless the server optimizes them).
Hogan
The design suggests the website can have few users, I've done query for design. Well, I agree sub-queries are slow. I use them only in FROM section if there is GROUP BY, because it makes code much more readable. I don't like this MySQL quirk, if field isn't in GROUP BY statement, I'll use aggregate function.
Michas