Как сделать быстрый подсчет на больших столах? - PullRequest
4 голосов
/ 06 октября 2010

У меня большие таблицы MySQL с сотнями тысяч строк.

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

например.

SELECT 'This week', COUNT(*) FROM customers 
WHERE sales_person_id = 1 AND DATEDIFF(NOW(), available_date) < 7

UNION

SELECT 'Next week', COUNT(*) FROM customers 
WHERE sales_person_id = 1 AND DATEDIFF(NOW(), available_date) >= 7 
    AND DATEDIFF(NOW(), available_date) < 14

UNION

... (a few more like this)

Написав аналогичный запрос для другой большой таблицы, я заметил, что изменение механизма с InnoDB на MyISAM значительно ускорило запрос (InnoDB не нужен для этих таблиц, так как они не проверяют внешний ключ). Могу ли я сделать что-нибудь еще, чтобы ускорить подсчет (например, индексировать соответствующие поля)?

Ответы [ 3 ]

5 голосов
/ 06 октября 2010

WHERE sales_person_id = 1 AND available_date BETWEEN CURDATE() - INTERVAL 1 WEEK AND CURDATE()

Выполнение этого должно позволить MySQL использовать составной индекс, созданный для столбцов (sales_person_id, available_date) (используйте EXPLAIN для проверки)

3 голосов
/ 06 октября 2010
  1. Никогда не делайте в нескольких запросах, что вы можете сделать в одном.

    Если вы создаете производную таблицу / встроенное представление с необходимыми датами начала и окончания, это можно использовать для получения желаемого результата в одном запросе с использованием GROUP BY. MySQL не имеет рекурсивной функции, поэтому вам нужно использовать табличный трюк NUMBERS для генерации дат ...

    1. Создать таблицу, содержащую только инкрементные числа - это легко сделать с помощью auto_increment:

      DROP TABLE IF EXISTS `example`.`numbers`;
      CREATE TABLE  `example`.`numbers` (
       `id` int(10) unsigned NOT NULL auto_increment,
        PRIMARY KEY  (`id`)
      ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
      
    2. Заполните таблицу, используя:

      INSERT INTO NUMBERS (id)
      VALUES (NULL)
      

      ... для столько значений, сколько вам нужно.

    3. Используйте DATE_ADD для построения списка дат, увеличивая дни на основе значения NUMBERS.id.

      SELECT x.start_dt,
             x.end_dt
        FROM (SELECT DATE_ADD(NOW(), INTERVAL n.id - 1 DAY) AS start_dt,
                     DATE_ADD(NOW(), INTERVAL n.id + 6 DAY) AS end_dt
                FROM `numbers` n
               WHERE DATE_ADD(NOW(), INTERVAL (n.id - 1) DAY) <= '2011-01-01') x
      
    4. ПРИСОЕДИНЯЙТЕСЬ к своей таблице данных на основе даты и времени:

        SELECT x.start_dt,
               x.end_dt,
               COUNT(*) AS num
          FROM (SELECT DATE_ADD(NOW(), INTERVAL n.id - 1 DAY) AS start_dt,
                       DATE_ADD(NOW(), INTERVAL n.id + 6 DAY) AS end_dt
                  FROM `numbers` n
                 WHERE DATE_ADD(NOW(), INTERVAL (n.id - 1) DAY) <= '2011-01-01') x
          JOIN CUSTOMERS c ON c.available_date BETWEEN x.start_dt
                                                   AND x.end_dt
      GROUP BY x.start_dt, x.end_dt
      
  2. Не используйте функции, выполняемые с фактическими данными столбца - IE: DATEDIFF(NOW(), *available_date*) - потому что база данных не может использовать индекс (если он существует) для столбца available_date, потому что данные были изменены из значения индекса.

1 голос
/ 06 октября 2010

Фокус на предложении WHERE.

  • Есть ли индекс в полях в предложении WHERE?
  • Можете ли вы заменить функцию datediff () на константу, она оценивается?за каждый ряд.
...