2010-10-05 2 views
4

У меня есть большие таблицы 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 не требуется для этих таблиц, поскольку они не имеют внешнего ключа проверки). Есть ли что-нибудь еще, что я могу сделать, чтобы ускорить такие подсчеты (кроме индексации соответствующих полей)?

+2

Просто совет, хотя в этом случае, вероятно, не имеет значения - всегда проверяйте, действительно ли вам нужно UNION, альтернатива - «UNION ALL» дешевле (на больших результирующих наборах это могут быть * порядки величин) – Unreason

+0

Кроме того, только ради насыщения - это ваш индекс на (available_date, sales_person_id)? – Unreason

ответ

5

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

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

+0

+1. Более общим является то, чтобы избежать использования вызовов функций в предложении where (в данном случае DATEDIFF), поскольку они вынуждают сканирование индекса, а не поиск индекса. Если вы думаете об этом, это имеет смысл - потому что результат функции не индексируется, поэтому каждая строка должна быть проверена. – womp

+0

+1. Да. Исходный запрос не является SARGable. –

+1

Спасибо за объяснение womp. Я вижу из вашего профиля, вы тоже живете в Виктории! –

1

Сосредоточьтесь на ИНЕКЕ.

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

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

    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. JOIN на свой стол данных на основе даты и времени части:

      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, потому что данные были изменены от значения индекса.

+0

Спасибо за идею. Я думаю, что в настоящий момент это слишком сложно для этого вопроса, но я буду учитывать эту технику, если ее нужно будет оптимизировать в будущем. –

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