Рассмотрим типичный оператор GROUP BY в SQL: у вас есть таблица типа
+------+-------+
| Name | Value |
+------+-------+
| A | 1 |
| B | 2 |
| A | 3 |
| B | 4 |
+------+-------+
И вы просите
SELECT Name, SUM(Value) as Value
FROM table
GROUP BY Name
Вы получите
+------+-------+
| Name | Value |
+------+-------+
| A | 4 |
| B | 6 |
+------+-------+
Вы можете себе представить, что SQL генерирует промежуточную отсортированную таблицу, такую как
+------+-------+
| Name | Value |
+------+-------+
| A | 1 |
| A | 3 |
| B | 2 |
| B | 4 |
+------+-------+
, а затем агрегирует вместе последовательные строки: столбцу «Значение» был присвоен агрегатор (в данном случае SUM), поэтому его легко агрегировать. Столбцу «Имя» не был дан агрегатор, и поэтому он использует то, что вы могли бы назвать «тривиальным частичным агрегатором»: учитывая две одинаковые вещи (например, А и А), он объединяет их в одну копию одного из входы (в данном случае А). При любом другом вводе он не знает, что делать, и вынужден начинать агрегирование заново (на этот раз со столбцом «Имя», равным B).
Я хочу сделать более экзотическую агрегацию. Мой стол выглядит как
+------+-------+
| Name | Value |
+------+-------+
| A | 1 |
| BC | 2 |
| AY | 3 |
| AZ | 4 |
| B | 5 |
| BCR | 6 |
+------+-------+
И предполагаемый вывод
+------+-------+
| Name | Value |
+------+-------+
| A | 8 |
| B | 13 |
+------+-------+
Откуда это? A и B являются «минимальными префиксами» для этого набора имен: они встречаются в наборе данных, и каждое имя имеет один из них в качестве префикса. Я хочу объединить данные, сгруппировав строки, когда их имена имеют одинаковый минимальный префикс (и, конечно, добавьте значения).
В предыдущей модели группировки игрушек промежуточная отсортированная таблица будет
+------+-------+
| Name | Value |
+------+-------+
| A | 1 |
| AY | 3 |
| AZ | 4 |
| B | 5 |
| BC | 2 |
| BCR | 6 |
+------+-------+
Вместо использования «тривиального частичного агрегатора» для Имен, мы бы использовали тот, который может агрегировать X и Y вместе, если X является префиксом Y; в этом случае он возвращает X. Таким образом, первые три строки будут объединены в строку с (Name, Value) = (A, 8), тогда агрегатор увидит, что A и B не могут быть объединены, и будет двигаться дальше в новый «блок» строк для агрегирования.
Хитрость заключается в том, что значение, которое мы группируем, является «нелокальным»: если бы A не было именем в наборе данных, то AY и AZ были бы минимальным префиксом. Оказывается, строки AY и AZ объединяются в одну и ту же строку в конечном выводе, но вы не можете знать этого, просто глядя на них изолированно.
Чудесным образом, в моем случае использования минимальный префикс строки может быть определен без ссылки на что-либо еще в наборе данных. (Представьте, что каждое из моих имен представляет собой одну из строк «привет», «мир» и «бар», за которыми следует любое количество символов z. Я хочу сгруппировать все имена с одним и тем же «базовым» словом вместе.)
На мой взгляд, у меня есть два варианта:
1) Простой вариант: вычислить префикс для каждой строки и группы по этому значению напрямую. К сожалению, у меня есть индекс по имени, и вычисление минимального префикса (длина которого зависит от самого имени) не позволяет мне использовать этот индекс. Это вызывает полное сканирование таблицы, которое слишком медленно.
2) Сложный вариант: каким-то образом убедить MySQL использовать «агрегатор частичного префикса» для Name. Это сталкивается с проблемой «нелокальности», описанной выше, но это нормально, если мы сканируем таблицу в соответствии с моим индексом по имени, поскольку тогда каждый минимальный префикс будет встречаться до любой из других строк, префиксом которой он является; мы никогда бы не попытались объединить AY и AZ вместе, если бы A был в наборе данных.
В декларативном языке программирования # 2 было бы довольно просто: извлекать строки по одной в алфавитном порядке, отслеживая текущий префикс. Если имя вашей новой строки имеет префикс, оно входит в список, который вы используете в данный момент. В противном случае, начните новый сегмент с этим в качестве префикса В MySQL я заблудился относительно того, как это сделать. Обратите внимание, что набор минимальных префиксов заранее неизвестен.