10.2.1.17 Оптимизация GROUP BY
Наиболее общий способ удовлетворения запроса GROUP BY — это сканирование всей таблицы и создание новой временной таблицы, где все строки каждой группы расположены последовательно, а затем использование этой временной таблицы для определения групп и применения агрегатных функций (если таковые имеются). В некоторых случаях MySQL может сделать гораздо лучше, чем это, и избежать создания временных таблиц, используя доступ к индексу.
Наиболее важными предпосылками для использования индексов для GROUP BY являются то, что все GROUP
BY столбцы ссылаются на атрибуты из одного и того же индекса, и что индекс хранит свои ключи в порядке (как это верно, например, для BTREE индекса, но не для HASH индекса). Возможность заменить использование временных таблиц доступом к индексу также зависит от того, какие части индекса используются в запросе, условий, указанных для этих частей, и выбранных агрегатных функций.
Существует два способа выполнения запроса GROUP BY через доступ к индексу, как подробно описано в следующих разделах. Первый метод применяет операцию группирования вместе со всеми предикатными условиями (если таковые имеются). Второй метод сначала выполняет сканирование диапазона, а затем группирует полученные кортежи.
Неполное сканирование индекса также может использоваться в отсутствие GROUP BY при некоторых условиях. См. Метод сканирования пропусков в диапазонах.
Неполное сканирование индекса
Наиболее эффективный способ обработки GROUP
BY — это когда индекс используется для непосредственного извлечения столбцов группирования. С этим методом доступа MySQL использует свойство некоторых типов индексов, что ключи упорядочены (например, BTREE). Это свойство позволяет использовать группы поиска в индексе без необходимости учитывать все ключи в индексе, удовлетворяющие всем WHERE условиям. Этот метод доступа учитывает только часть ключей в индексе, поэтому он называется Неполным сканированием индекса. Когда нет условия WHERE, неполное сканирование индекса считывает столько ключей, сколько групп, что может быть намного меньше, чем всех ключей. Если условие WHERE содержит предикаты диапазона (см. обсуждение типа соединения range в Раздел 10.8.1, «Оптимизация запросов с помощью EXPLAIN»), неполное сканирование индекса ищет первый ключ каждой группы, удовлетворяющей условиям диапазона, и снова считывает минимальное количество ключей. Это возможно при следующих условиях:
Запрос выполняется по одной таблице.
Имена столбцов
GROUP BYтолько столбцы, которые образуют левосторонний префикс индекса и никакие другие столбцы. (Если вместоGROUP BY, запрос имеет условиеDISTINCT, все различные атрибуты относятся к столбцам, которые образуют левосторонний префикс индекса.) Например, если в таблицеt1есть индекс по(c1,c2,c3), неполное сканирование индекса применимо, если запрос имеетGROUP BY c1, c2. Оно неприменимо, если запрос имеетGROUP BY c2, c3(столбцы не являются левосторонним префиксом) илиGROUP BY c1, c2, c4(c4отсутствует в индексе).В списке выбора (если таковой имеется) используются только агрегатные функции
MIN()иMAX(), и все они относятся к одному столбцу. Столбец должен находиться в индексе и должен сразу следовать за столбцами вGROUP BY.Любые другие части индекса, кроме тех, которые из
GROUP BY, упомянутых в запросе, должны быть константами (то есть они должны быть указаны в равенствах с константами), за исключением аргумента функцийMIN()илиMAX().Для столбцов в индексе должны быть индексированы полные значения столбцов, а не просто префикс. Например, с
c1 VARCHAR(20), INDEX (c1(10)), индекс использует только префикс значенийc1и не может использоваться для неполного сканирования индекса.
Если неполное сканирование индекса применимо к запросу, вывод EXPLAIN показывает Using index for group-by в столбце Extra.
Предположим, что есть индекс idx(c1,c2,c3) по таблице t1(c1,c2,c3,c4). Метод доступа Неполное сканирование индекса может использоваться для следующих запросов:
SELECT c1, c2 FROM t1 GROUP BY c1, c2;
SELECT DISTINCT c1, c2 FROM t1;
SELECT c1, MIN(c2) FROM t1 GROUP BY c1;
SELECT c1, c2 FROM t1 WHERE c1 < const GROUP BY c1, c2;
SELECT MAX(c3), MIN(c3), c1, c2 FROM t1 WHERE c2 > const GROUP BY c1, c2;
SELECT c2 FROM t1 WHERE c1 < const GROUP BY c1, c2;
SELECT c1, c2 FROM t1 WHERE c3 = const GROUP BY c1, c2;
Следующие запросы не могут быть выполнены с помощью этого быстрого метода выбора по причинам, указанным ниже:
-
Существуют агрегатные функции, отличные от
MIN()илиMAX():SELECT c1, SUM(c2) FROM t1 GROUP BY c1;
-
Столбцы в условии
GROUP BYне образуют левостороннего префикса индекса:SELECT c1, c2 FROM t1 GROUP BY c2, c3;
-
Запрос ссылается на часть ключа, которая следует за
GROUP BYчастью, и для которой нет равенства с константой:SELECT c1, c3 FROM t1 GROUP BY c1, c2;
Если бы запрос включал
WHERE c3 =, неполное сканирование индекса могло бы использоваться.const
Метод доступа Неполное сканирование индекса может применяться к другим формам ссылок на агрегатные функции в списке выбора, помимо ссылок MIN() и MAX(), которые уже поддерживаются:
Поддерживаются
AVG(DISTINCT),SUM(DISTINCT)иCOUNT(DISTINCT).AVG(DISTINCT)иSUM(DISTINCT)принимают один аргумент.COUNT(DISTINCT)может иметь более одного столбца аргумента.В запросе не должно быть условия
GROUP BYилиDISTINCT.Ограничения неполного сканирования индекса, описанные ранее, по-прежнему применяются.
Предположим, что есть индекс idx(c1,c2,c3) по таблице t1(c1,c2,c3,c4). Метод доступа Неполное сканирование индекса может использоваться для следующих запросов:
SELECT COUNT(DISTINCT c1), SUM(DISTINCT c1) FROM t1;
SELECT COUNT(DISTINCT c1, c2), COUNT(DISTINCT c2, c1) FROM t1;
Полное сканирование индекса
Полное сканирование индекса может быть либо полным сканированием индекса, либо сканированием индекса по диапазону, в зависимости от условий запроса.
Когда условия для неполного сканирования индекса не выполнены, все равно возможно избежать создания временных таблиц для запросов GROUP BY. Если в условии WHERE есть условия диапазона, этот метод считывает только ключи, удовлетворяющие этим условиям. В противном случае он выполняет сканирование индекса. Поскольку этот метод считывает все ключи в каждом диапазоне, определенном условием WHERE, или сканирует весь индекс, если нет условий диапазона, он называется Полным сканированием индекса. При полном сканировании индекса операция группирования выполняется только после того, как все ключи, удовлетворяющие условиям диапазона, были найдены.
Для работы этого метода достаточно, чтобы для всех столбцов в запросе, относящихся к частям ключа, предшествующим или расположенным между частями ключа GROUP BY, было условие равенства с константой. Константы из условий равенства заполняют любые «пробелы» в ключах поиска, так что можно сформировать полные префиксы индекса. Эти префиксы индекса затем могут использоваться для поиска в индексе. Если результат GROUP BY требует сортировки, и можно сформировать ключи поиска, которые являются префиксами индекса, MySQL также избегает дополнительных операций сортировки, потому что поиск с префиксами в упорядоченном индексе уже извлекает все ключи в порядке.
Предположим, что есть индекс idx(c1,c2,c3) по таблице t1(c1,c2,c3,c4). Следующие запросы не работают с методом доступа Неполное сканирование индекса, описанным ранее, но все еще работают с методом доступа Полное сканирование индекса.
-
Есть пробел в
GROUP BY, но он покрывается условиемc2 = 'a':SELECT c1, c2, c3 FROM t1 WHERE c2 = 'a' GROUP BY c1, c3;
-
GROUP BYне начинается с первой части ключа, но есть условие, которое предоставляет константу для этой части:SELECT c1, c2, c3 FROM t1 WHERE c1 = 'a' GROUP BY c2, c3;
© 2025 Oracle
Licensed under the GPLv2 License.