在php和mysql中需要关于嵌套循环查询的帮助?

时间:2010-04-21 05:22:56

标签: php mysql optimization nested-loops

我想要做到这一点:

<?php
  $good_customer = 0;
  $q = mysql_query("SELECT user FROM users WHERE activated = '1'"); // this gives me about 40k users

  while($r = mysql_fetch_assoc($q)){
    $money_spent = 0;

    $user = $r['user'];
    // Do queries on another 20 tables
    for($i = 1; $i<=20 ; $i++){
      $tbl_name = 'data' . $i;

      $q2 = mysql_query("SELECT money_spent FROM $tbl_name WHERE user = '{$user}'");
      while($r2 = mysql_fetch_assoc($q2)){
        $money_spend += $r2['money_spent'];
      }

      if($money_spend > 1000000){
        $good_customer += 1;
      }
    }
  }

这只是一个例子。我在localhost上测试,对于单个用户,它返回非常快。但是当我尝试1000时,它需要永远,甚至没有提到40k用户。

无论如何要优化/改进此代码?

编辑: 顺便说一下,其他20个表中的每个表都有大约20到40k的记录

EDIT2:

好的,放弃“花钱”的想法。这是我目前的结构:

user table =&gt;用户是PK

logs_week_1 table =&gt;用户是FK。

logs_week_2 table =&gt;用户是FK

logs_week_3 table =&gt;用户是FK

...将来会有更多的日志表。

我想找到他们在我的网站上花费的“平均时间”,该时间存储在每个日志表中。

所以你们这么说,每周存储日志是一个坏主意?我应该合并到一个表中?

4 个答案:

答案 0 :(得分:2)

听起来你的模型有问题。为什么你有20个data - 表而不是一个week - 列?

然后你可以做一个

Select user, Sum( money_spent ) As total_money_spent
From data
Group By user

甚至

Select Count(*) As good_customer_count
From data
Group By user
Having Sum( money_spent ) > 1000000

使用您当前的结构,您只能执行以下操作:

Select u.user, d1.money_spent + d2.money_spent + ...
From users u
Join data1 d1 On ( d1.user = u.user )
Join data2 d2 On ( d2.user = u.user )
...

Select Count(*) As good_customer_count
From
  ( Select d1.money_spent + d2.money_spent + ... As total_money_spent
    From data1 d1
    Join data1 d1 On ( d1.user = u.user )
    Join data2 d2 On ( d2.user = u.user )
    ...
  )
Where total_money_spent > 1000000

这肯定会比您当前的解决方案更快。


页面上花费的时间应存储在数字字段中。

答案 1 :(得分:1)

正如Peter已经给出了一个很好的答案,我将只发布查询如何通过适当的设计(所有日志数据在一个表中)看起来

SELECT user, AVG(TIMEDIFF(start_time, end_time)) AS average_time
FROM logs
GROUP BY user

您可以进一步申请上述条件以获取统计数据仅限一段时间(周,月等),或者您也可以按其他级别分组。

您还可以在同一查询中获得MAX和COUNT(以及标准差和other aggregate function)。

当然,请使用较大的数据集来处理索引以获得最佳性能。

编辑:

就像我给彼得+1一样,我注意到他没有提到UNION ALL选项

所以,你可以(这不是最优的,并不与其他人提出的设计问题警告相矛盾)

SELECT user, AVG(TIMEDIFF(start_time, end_time)) AS average_time
FROM (
    SELECT * FROM log_week_1
    UNION ALL
    SELECT * FROM log_week_2
    UNION ALL
    SELECT * FROM log_week_3
    ...
) U
GROUP BY user

你也可以为这个联盟创建一个VIEW。

答案 2 :(得分:0)

您应该将在网站上花费的时间存储为数字(以分钟或秒为单位),而不是时间。然后,您可以计算此值的平均值和总和。并将您的日志保存在一个表中。

答案 3 :(得分:0)

对于40k用户,您正在创建1 + 20 * 40k查询。无论如何这都会很慢。停止将日志保存在20个表中。您应该以另一种方式设计数据库。在适当设计的数据库上,这应该通过1个查询完成

SELECT count(user) as good_customers FROM users JOIN $tbl_name ON users.user = {$tbl_name}.user ON WHERE users.activated = '1' HAVING SUM(money_spent) > 100000.

在最糟糕的情况下,您还应该对每个表执行1次查询。

SELECT user, SUM(money_spent) as money_spent FROM users JOIN $tbl_name ON users.user = {$tbl_name}.user ON WHERE users.activated = '1'.

然后总结这20个money_spent列,你就得到了答案。