开发者

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 your Products table, create AFTER INSERT and AFTER DELETE triggers on the Likes table (assuming you never UPDATE that table) that increases the like count of a product when a new row is inserted to the Likes table and decreases it when a row is deleted. Than, add a cronjob that executes UPDATE 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 like CREATE TABLE Products_Likes_Cache (product_id INTEGER PRIMARY KEY, likes_count INTEGER NOT NULL);. Than, add a cronjob that executes BEGIN; 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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜