Получить скорость роста из таблицы заказов MySQL, но слишком медленно - PullRequest
2 голосов
/ 04 июня 2011

У меня есть таблица заказа продукции в mysql.Это примерно так:

create table `order`
  (productcode int,
   quantity tinyint,
   order_date timestamp,
   blablabla)

затем, чтобы получить скорость роста, я написал этот запрос:

SELECT thismonth.productcode,
       (thismonth.ordercount-lastmonth.ordercount)/lastmonth.ordercount as riserate
  FROM ( (SELECT productcode,
                 sum(quantity) as ordercount
            FROM `order`
            where date_format(order_date,'%m') = 12
            group by productcode) as thismonth,
          (SELECT productcode,
                  sum(quantity) as ordercount
             FROM `order`
             where date_format(order_date,'%m') = 11
             group by productcode) as lastmonth)
WHERE thismonth.productcode = lastmonth.productcode
ORDER BY riserate;

, но на моем компьютере он работает около 30 секунд (200000 записей, 200 МБ (включаядругие поля)).Есть ли способ увеличить скорость запроса?Я уже создаю индекс для поля кода продукта.

Я думал, что причиной низкой производительности является 'GROUP BY', есть ли другой способ?

Я попробовал ваши ответы, но все они, кажется, нетработать, и мне было интересно, если что-то не так с индексом (это не я, кто их создал), поэтому я удаляю весь индекс и воссоздаю их, все идет хорошо - это занимает всего 3-4 секунды.И разница между моим запросом и вашим не очень очевидна.Но ДЕЙСТВИТЕЛЬНО спасибо вам, ребята, я многому научился :)

Ответы [ 5 ]

3 голосов
/ 04 июня 2011

Попробуйте добавить индекс (ORDER_DATE, PRODUCTCODE) и измените запрос, чтобы исключить использование функции DATE_FORMAT, например:

SELECT thismonth.productcode,
       (thismonth.ordercount-lastmonth.ordercount)/lastmonth.ordercount as riserate   
  FROM ( (SELECT productcode,
                 sum(quantity) as ordercount
            FROM `order`
            WHERE ORDER_DATE BETWEEN '01-12-2010' AND '31-12-2010'
                  GROUP BY PRODUCTCODE) as thismonth,
         (SELECT productcode,
                 sum(quantity) as ordercount
            FROM `order`
            WHERE ORDER_DATE BETWEEN '01-11-2010' AND '30-11-2010'
            group by productcode) as lastmonth)
  WHERE thismonth.productcode = lastmonth.productcode
  ORDER BY riserate;

Делитесь и наслаждайтесь.

0 голосов
/ 05 июня 2011
  • Попробуйте использовать дату и время вместо отметки времени

Если единственной причиной использования отметки времени является автоматическое значение по умолчанию при вставке и обновлении, используйте вместо этого дату и вставьте now () в вставки.и обновления или использовать триггеры.Отметка времени дает вам дополнительное преобразование для часовых поясов, но если у вас нет клиентов, подключающихся к вашей базе данных из разных часовых поясов, вы просто теряете время на преобразования.Одно это должно дать вам ускорение на 15-30%.

  • Это может быть один из редких случаев, когда оптимизатор может выбрать неправильный индекс

И индекс кода продукта в этом неверендело.Поскольку вы группируете по коду продукта и используете где для другого столбца, который не очень избирателен, оптимизатор может подумать, что использование индекса для кода продукта может ускорить процесс.Но с использованием этого индекса он дает вам очень случайное сканирование с помощью поиска по индексу, но все же с довольно большим количеством строк, вместо более быстрого последовательного полу-полного сканирования без него, но с индексом order_date, чтобы ограничить число сканируемых строк.Оптимизатор просто не знает, что можно ожидать, что строки будут в основном отсортированы по порядку_даты на диске, а не по коду продукта.Конечно, чтобы индекс order_date работал, вы должны изменить свой запрос, чтобы при каждом сравнении с использованием имени столбца order_date было с одной стороны от =, <,> или BETWEEN, а с другой стороны - постоянные значения, как это предложил Боб Джавис в своем ответе.(+1 к нему).Так что вы можете попробовать его запрос слегка модифицированным, с исправленными форматами даты и принудительным использованием индекса order_date - при условии, что он у вас есть, если нет, вам действительно нужно добавить его с помощью

ALTER TABLE `order` ADD INDEX order_date( order_date );

Так что итоговый запрос должен выглядетьнапример:

SELECT thismonth.productcode,
       (thismonth.ordercount-lastmonth.ordercount)/lastmonth.ordercount as riserate   
  FROM ( (SELECT productcode,
                 sum(quantity) as ordercount
            FROM `order` FORCE INDEX( order_date )
            WHERE order_date BETWEEN '2010-12-01' AND '2010-12-31'
            GROUP BY productcode) as thismonth,
         (SELECT productcode,
                 sum(quantity) as ordercount
            FROM `order` FORCE INDEX( order_date )
            WHERE order_date BETWEEN '2010-11-01' AND '2010-11-30'
            group by productcode) as lastmonth)
  WHERE thismonth.productcode = lastmonth.productcode
  ORDER BY riserate;

Если не использовать индекс productid, это даст вам некоторую скорость (полное сканирование должно быть быстрее), а использование индекса order_date еще больше, в зависимости от того, сколько строк удовлетворяет условиям order_date по сравнению со всеми строками в таблице.

0 голосов
/ 04 июня 2011
SELECT
  productcode,
  (this_month_count - last_month_count) / last_month_count AS riserate
FROM (
  SELECT
    o.product,
    SUM(CASE MONTH(o.order_date) WHEN MONTH(m.date_start) THEN o.quantity END) AS last_month_count,
    SUM(CASE MONTH(o.order_date) WHEN MONTH(m.date_end)   THEN o.quantity END) AS this_month_count
  FROM `order` o
    INNER JOIN (
      SELECT
        CAST('2010-11-01' AS date) AS date_start,
        CAST('2010-12-31' AS date) AS date_end
    ) m ON o.order_date BETWEEN m.date_start AND m.date_end
    GROUP BY o.product
) s
0 голосов
/ 04 июня 2011

@ Решение Боба Джарвиса может решить вашу проблему со скоростью. Если нет, или если вы хотите попробовать альтернативу:

  1. Добавить update_month для сохранения месяца дата обновления
  2. Обновить столбец для существующих строк
  3. Добавить индекс на update_month
  4. Создайте триггер BEFORE UPDATE для установить значение update_month в строке Обновления
  5. Создать триггер ДО ВСТАВКИ для установить значение update_month в строке Вставки
  6. Измените ваш запрос соответственно
0 голосов
/ 04 июня 2011

Учитывая огромное количество данных, с которыми вы, похоже, работаете, оптимизация может быть затруднена.Сначала я посмотрю, как вы используете поле order_date.Вероятно, его следует проиндексировать с помощью поля product_code.Я также не думаю, что date_format - лучший способ вывести месяц из даты - MONTH (order_date) почти наверняка будет быстрее.Много раз я создавал новую таблицу для исторических данных и заполнял ее результатами ваших внутренних запросов.Поскольку это исторические данные, вам не нужно постоянно получать самые последние данные.Поскольку вам не придется вычислять исторические данные каждый раз, когда вы выполняете запрос, он будет выполняться намного быстрее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...