2010-04-21 3 views
0

Я пытаюсь получить это сделать:Нужна помощь по вложенному циклу запросов в php и mysql?

<?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 - 40k записи

EDIT2:

Хорошо, отказаться от идеи "деньги тратить". Это мои нынешние структуры:

пользовательская таблица => пользователь PK

logs_week_1 таблица => пользователь FK.

logs_week_2 таблица => пользователь FK

logs_week_3 таблица => пользователь FK

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

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

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

+0

Почему вы делаете запросы на 20 разных таблицах? Кажется, ваша проблема в дизайне вашей базы данных. Расскажите, как выглядят структуры ваших таблиц. – Galen

+0

Ох .. Эти 20 +++ таблиц хранятся в таблицах журналов в неделю – mysqllearner

+0

БОЛЬШАЯ КРАСНАЯ КНОПКА СИГНАЛА. ОШИБКА ДИЗАЙНА БАЗЫ ДАННЫХ. Но это не редкость. Найдите «SQL Pessimization» и «SQL Antipattern». Если вам нужны данные за одну неделю, у вас нет недельных таблиц. Если у вас слишком много данных, вам нужна база данных OLAP. – dkretz

ответ

2

Похоже, что у вас есть проблемы с вашей моделью. Почему у вас есть 20 data -tables вместо одного с 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 

Это, безусловно, будет быстрее, чем ваше текущее решение.


И время, проведенное на странице, должно храниться в числовом поле.

+0

, если money_spent является «временным» типом. Как мне сделать сумму? Например, 00:10:23, 00:12:01 и т. Д. – mysqllearner

+0

Думаю, нам нужна информация о ваших структурах таблиц и о ваших данных, чтобы ответить на них. –

+0

'money_spent' мне понравился какой-то номер колонки ... –

0

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

1

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

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

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

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

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

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

@Unreason: используя Union и INNER Join? В настоящее время я использую UNION, результат кажется мне странным. Я пытаюсь использовать INNER Присоединяйтесь сейчас – mysqllearner

+1

@mysqllearner: если ваше внутреннее соединение равно 1-1, а затем соединяет ваши таблицы рядом друг с другом, вы можете выбрать все столбцы из всех таблиц в одной строке.если вы используете join, количество столбцов остается одинаковым (должно быть одинаковым), и результаты добавляются один за другим (с большой разницей в производительности между UNION и UNION ALL: UNION будет возвращать уникальные строки, и для этого потребуется создать индекс, UNION ALL может возвращать повторяющиеся записи, но я предположил, что ваши журналы не перекрываются). – Unreason

+0

@mysqllearner: Я опубликовал решение UNION ALL, потому что он концептуально эквивалентен (для выбора), чтобы зафиксировать ваш дизайн - ведение всех журналов в одной таблице (но все же это не то же самое представление). – Unreason

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

Смежные вопросы