Нужна помощь по вложенному циклу запросов в php и mysql? - PullRequest
0 голосов
/ 21 апреля 2010

Я пытаюсь заставить это сделать:

<?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 пользователей.

В любом случае, чтобы оптимизировать / улучшить этот код?

EDIT: Кстати, каждая из 20 других таблиц имеет ~ 20 - 40 тыс. Записей

EDIT2:

Хорошо, отбросьте идею "потратить деньги". Это мои текущие структуры:

таблица пользователей => пользователь PK

logs_week_1 table => пользователь FK.

logs_week_2 table => пользователь FK

logs_week_3 table => пользователь FK

... в будущем будет больше таблиц журналов.

Я хочу узнать «среднее время», которое они проводят на моем сайте, и сколько времени хранится в каждой из таблиц журналов.

Так вы, ребята, говорили, что еженедельное хранение журналов - плохая идея? Я должен слить в одну таблицу?

Ответы [ 4 ]

2 голосов
/ 21 апреля 2010

Похоже, у вас проблемы с вашей моделью. Почему у вас 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 голос
/ 21 апреля 2010

Поскольку Питер уже дал хороший ответ, я буду публиковать только то, как запрос будет выглядеть с правильным дизайном (все данные журнала в одной таблице)

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

Вы можете в дальнейшем применять условия, указанные выше, чтобы получать статистику только за определенный период (неделю, месяц и т. Д.), Или вы также можете группировать по другому уровню.

Вы также можете получить MAX и COUNT в том же запросе (а также стандартное отклонение и другую статистическую функцию ) эффективным способом.

Конечно, позаботьтесь о своих индексах для лучшей производительности с большими наборами данных.

EDIT:

Так же, как я давал Петру +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

А также вы можете создать ВИД для этого союза.

0 голосов
/ 11 декабря 2012

Для пользователей 40k вы создаете запросы 1 + 20 * 40k. Это будет медленно в любом случае. Прекратите вести журналы в 20 таблицах. Вы должны разработать свою базу данных по-другому. В правильно спроектированной базе данных все это должно быть выполнено одним запросом

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, и вы получите ответ.

0 голосов
/ 21 апреля 2010

Вы должны хранить время, проведенное на вашем сайте, как число (в минутах или секундах), а не как время. Затем вы можете рассчитать средние и суммы по этому значению. И храните свои журналы в одной таблице.

...