2009-07-07 4 views
0

Я ищу советы по лучшей практике, как ускорить запросы и в то же время минимизировать накладные расходы, необходимые для вызова функций date/mktime. Для того, чтобы упрощать проблему, я имею дело со следующей сервировки стола:Как свести к минимуму нагрузку в запросах, требующих группировки с различными инверторами?

CREATE TABLE my_table(
    id INTEGER PRIMARY KEY NOT NULL AUTO_INCREMENT, 
    important_data INTEGER, 
    date INTEGER); 

Пользователь может выбрать, чтобы показать 1) Все записи между двумя датами:

SELECT * FROM my_table 
    WHERE date >= ? AND date <= ? 
    ORDER BY date DESC; 

Выход:

10-21-2009 12:12:12, 10002 
10-21-2009 14:12:12, 15002 
10-22-2009 14:05:01, 20030 
10-23-2009 15:23:35, 300 
.... 

Я не думаю, что в этом случае многое улучшится.

2) Обобщить/группы выходной день, неделя, месяц, год:

SELECT COUNT(*) AS count, SUM(important_data) AS important_data 
    FROM my_table 
    WHERE date >= ? AND date <= ? 
    ORDER BY date DESC; 

Пример вывода в месяц:

10-2009, 100002 
11-2009, 200030 
12-2009, 3000 
01-2010, 0 /* <- very important to show empty dates, with no entries in the table! */ 
.... 

Чтобы выполнить вариант 2) Я в настоящее время работает очень дорогостоящий для цикла с mktime/date примерно:

for(...){ /* example for group by day */ 
    $span_from = (int)mktime(0, 0, 0, date("m", $time_min), date("d", $time_min)+$i, date("Y", $time_min)); 
    $span_to = (int)mktime(0, 0, 0, date("m", $time_min), date("d", $time_min)+$i+1, date("Y", $time_min)); 
    $query = ".."; 
    $output = date("m-d-y", ..); 
} 

Каковы мои идеи до сих пор? Добавьте дополнительные/избыточные столбцы (INTEGER) за день (20091212), месяц (200912), неделю (200942) и год (2009). Таким образом, я могу избавиться от всех ненужных запросов в цикле for. Однако я все еще сталкиваюсь с проблемой, чтобы очень быстро вычислить все даты, которые не имеют эквивалента в базе данных. Один из способов просто переместить проблему может состоять в том, чтобы позволить MySQL выполнить задание и просто использовать один большой запрос (рассчитать все даты/использовать функции даты MySQL) с левым соединением (данными). Было бы разумным позволить MySQL взять дополнительную нагрузку? В любом случае я не хочу использовать все эти mktime/date в цикле for. Поскольку у меня есть полный контроль над табличной планировкой и кодом, даже приветствуются предложения с серьезными изменениями!

Update

Благодаря Greg я придумал следующий запрос SQL. Однако она по-прежнему ошибок мне использовать 50 строк SQL заявления - создать с PHP - что, возможно, можно было бы сделать быстрее и элегантнее иначе:

SELECT * FROM ( 
    SELECT DATE_ADD('2009-01-30', INTERVAL 0 DAY) AS day UNION ALL 
    SELECT DATE_ADD('2009-01-30', INTERVAL 1 DAY) AS day UNION ALL 
    SELECT DATE_ADD('2009-01-30', INTERVAL 2 DAY) AS day UNION ALL 
    SELECT DATE_ADD('2009-01-30', INTERVAL 3 DAY) AS day UNION ALL 
    ...... 
    SELECT DATE_ADD('2009-01-30', INTERVAL 50 DAY) AS day) AS dates 
LEFT JOIN (
    SELECT DATE_FORMAT(date, '%Y-%m-%d') AS date, SUM(data) AS data 
    FROM test 
    GROUP BY date 
) AS results 
ON DATE_FORMAT(dates.day, '%Y-%m-%d') = results.date; 

ответ

1

Вы определенно не должны делать запрос внутри цикла. Вы можете сгруппировать следующим образом:

SELECT COUNT(*) AS count, SUM(important_data) AS important_data, DATE_FORMAT('%Y-%m', date) AS month 
    FROM my_table 
    WHERE date BETWEEN ? AND ? -- This should be the min and max of the whole range 
    GROUP BY DATE_FORMAT('%Y-%m', date) 
    ORDER BY date DESC; 

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

+0

Спасибо за быстрый ответ! Мне потребуется некоторое время, чтобы проверить вашу идею, потому что мне нужно переключиться с INTEGER на DATETIME, чтобы успешно использовать DATE_FORMAT. Однако это выглядит многообещающе. Если я не ошибаюсь, чего-то не хватает, как мне получить все эти даты без записей в таблице? Например: у меня есть данные за 2009-10 и 2009-11 годы, но 2009-12 пуст, но должен быть заметен тем не менее ..должен ли я вернуться к функциям PHP для этого или есть ли для этого элегантное решение mysql? – merkuro

+0

@merkuro: Вы можете выводить только данные * там *. Отсутствующие записи не будут волшебным образом появляться. Если вам нужно иметь запись за каждый день года, сделайте таблицу календаря и сделайте внешнее соединение против этого. Такая таблица должна быть создана/заполнена только один раз, и ей требуется всего 365 рядов в год. Таким образом, любые «пробелы» исчезнут на выходе. – Tomalak

+0

@Tomalak Вы имеете в виду, что в установках MySQL нет встроенного волшебника? Честно говоря, с какой целью были ваши смешные комментарии? Я уже упоминал о возможности использования левого соединения, хотя я вижу потенциальную проблему с производительностью в этом решении из-за увеличения использования диска. В любом случае я надеялся, что кто-то придумает хороший диапазон/массив, как функция, которую я упустил. – merkuro

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

  • Нет связанных вопросов^_^