2010-01-27 56 views
3

我使用MySQL(MyISAM数据)5.0.41和我有这个疑问:SQL计数多对多的值还是每次添加新行时都计数?

SELECT `x`.`items`.id, `x`.`items`.name, COUNT(*) AS count 
    FROM `x`.`items` INNER JOIN `x`.`user_items` 
    ON `x`.`items`.id = `x`.`user_items`.item_id 
    GROUP BY name HAVING count > 2 ORDER BY count DESC 

我有36,000用户175,000 user_items和不断增加60,000项。所以这个查询是有点慢......

是更好地:

  • 有在items一个count场和更新定期(比如每次用户将商品)
  • 或运行这样的查询(缓慢)..

或者是否有任何SQL将填充计数字段为我?

感谢

+1

数据库系统?版?你有什么指数? – 2010-01-27 15:21:25

+0

已编辑以显示数据库系统和版本。 – betamax 2010-01-27 15:31:28

回答

2

您应该将user_items.item_id编入索引并对其进行分组而不是名称。字符串的分组速度要慢得多(为自己尝试一下),并且索引应该加快速度。如果仍然太慢,您可以先运行GROUP BY查询,然后加入项目表,如果您的DBMS执行计划默认情况下没有这样做。

+0

我按items_id分组,它增加了大约250ms的速度。你是什​​么意思索引'user_items.item_id'? – betamax 2010-01-27 15:45:04

+0

请查看http://dev.mysql.com/doc/refman/5.0/en/create-index.html,了解如何使用它。如果你可以得到你的MySQL服务器管理员应用程序,你应该可以从那里做到。查看http://en.wikipedia.org/wiki/Index_(database)是否需要关于数据库索引的一些信息。 – 2010-01-27 15:57:14

+0

另外,它相对于什么增加了250ms的速度?需要多长时间,现在需要多长时间? – 2010-01-27 15:57:57

0

我的冲动将离开数据像正常形式(换句话说,不增加一个“计数”字段),然后缓存在慢速查询结果应用级别。

如果缓存是无效的,因为很多人都在做查询,很少有人做两次,然后,是的,你可以建立一个存储过程,在一些表格会自动更新一些行。细节因数据库供应商而异。这里是how to do it in Postgresql。由于竞争条件,这是执行它的唯一安全方式(即在数据库中,而不是从应用程序层中)。

+0

我认为缓存似乎是最好的解决方案。我有点不清楚如何/何时缓存?作为一个cron工作?一个小时或类似的东西?如果这有帮助;我正在使用Django。 – betamax 2010-01-27 15:37:01

0

您是否确实每次运行查询时都会获得所有36,000个用户?如果您正在寻找性能问题的根源,那么这可能就是它的原因。

根据您的RDBMS,您可以查看索引或物化视图等内容。将count作为表的一部分并试图维护它几乎肯定会是一个错误,尤其是对于数据库的小尺寸。

3

您可以使用一个中间的解决方案:

  • 一个ts DATETIME列添加到user_items表,将描述该用户加入该项目的时候

  • 一个ts DATETIME列添加到users表,将描述实际的点,只要cnt,缓存的计数列

  • 定期

    INSERT 
    INTO users (id, ts, cnt) 
    SELECT * 
    FROM (
         SELECT user_id, NOW() AS nts, COUNT(*) AS ncnt 
         FROM user_items ui 
         WHERE ui.timestamp <= NOW() 
         ) 
    ON DUPLICATE KEY 
    UPDATE ts = nnow, 
         cnt = ncnt 
    
  • 无效用户的时间戳当user_items条目被删除

  • 发行此查询计数项目:

    SELECT u.id, u.cnt + 
         (
         SELECT COUNT(*) 
         FROM user_items ui 
         WHERE ui.ts > u.ts 
           AND ui.user_id = u.id 
         ) 
    FROM users 
    
与新的计数和时间戳更新 users

这样,只有新增的项目才会被计入user_items表中, r,并且您不会经常更新记录时遇到并发问题。

+0

结果集正在查找'items.id','items.name'和每个项目有多少用途的计数。 – 2010-01-27 16:10:51

+0

'@ NickLarsen':然后将'ts'和'cnt'列添加到'items',而不是''users',并将其放到查询中。如果事实上,你可以做到这两个,只是更新和无效的两个表。 – Quassnoi 2010-01-27 16:13:15

+0

@NickLarsen没错。我并不是说这种方法不工作或者是错误的,但我认为我的数据库模式正在工作,这些变化可能会为我造成不必要的问题/工作。 – betamax 2010-01-27 16:15:02

1

该查询几乎每次都在进行全表扫描。这是没有办法的。索引会加速我加速连接的速度,但随着数据增长,查询会变得越来越慢。

存储摘要数据,如“计数”与“项目”将是要走的路。您可以使用存储过程或代码执行此操作。作为一个双重检查,您可以定期(即每天一次)更新所有计数,以确保它们准确无误。