How to aggregate related object counts in a view effectively?
Imagine this scenario:
I have three tables in my database: Products, Users and Likes, the latter representing a relation between a Product and a User. Now I have a query, which joins Products with the Likes table, co开发者_如何学Pythonunting how many Likes a Product got.
In fact, I need the information about the count much more often than the actual users and I want to use the query above as part of a greater query in a view. Is it possible to optimize the query or the view so that MySQL somehow caches the result of the count query above?
I don't know of a way to do that with mysql (but I mainly use postgres, so it might be possible and I just don't know). I would suggest two options:
- If you need the cache to be as up-to-date as possible, add an
likes_count
column to yourProducts
table, createAFTER INSERT
andAFTER DELETE
triggers on theLikes
table (assuming you never UPDATE that table) that increases the like count of a product when a new row is inserted to theLikes
table and decreases it when a row is deleted. Than, add a cronjob that executesUPDATE Products SET likes_count=(SELECT COUNT(1) FROM Likes WHERE product_id = Products.id)
once in a while to make sure the values are really up-to-date - maintaining the counts with triggers is never 100% accurate. - Another option is create a view as
CREATE VIEW Products_Likes_View AS SELECT product_id, COUNT(*) AS likes_count FROM Likes GROUP BY product_id;
, and create a cache table, something likeCREATE TABLE Products_Likes_Cache (product_id INTEGER PRIMARY KEY, likes_count INTEGER NOT NULL);
. Than, add a cronjob that executesBEGIN; TRUNCATE Products_Likes_Cache; INSERT INTO Products_Likes_Cache SELECT * FROM Products_Likes_View; COMMIT;
which'll sync the cache table with the up-to-date information from the view. Than, if you need accurate results, you can get the data directly from the view. Otherwise, use the cache table.
If you go with the first option, the triggers should look something like this (my MySQL skills are a bit rusty, I might be off with the exact syntax):
CREATE TRIGGER product_increase_likes AFTER INSERT ON Likes
FOR EACH ROW BEGIN
UPDATE Products SET likes_count=likes_count+1 WHERE id=NEW.product_id
END;
CREATE TRIGGER product_decrease_likes AFTER DELETE ON Likes
FOR EACH ROW BEGIN
UPDATE Products SET likes_count=likes_count-1 WHERE id=OLD.product_id
END;
You shouldn't need a join to count the likes for a product.
SELECT product, count(*)
FROM likes
GROUP BY product;
Should be ripping fast if you use a WHERE clause.
精彩评论