Если вы получаете неудовлетворительную производительность с индексом на месте, и ваша база данных доступна только для чтения, вы можете рассмотреть метод, который я упомянул в комментариях: прекомпретировать суммы для кусков. Он идет на один шаг дальше, чем разбиение на разделы: разбиение будет способно вычислять вашу сумму параллельно, но предварительно рассчитанные суммы превзошли его с большим отрывом. Идеальный размер куска находится где-то вокруг квадратного корня из числа строк в вашей таблице.
Скажите, что это ваш стол:
CREATE TABLE foo (
user INTEGER AUTO_INCREMENT PRIMARY KEY,
cnt INTEGER
);
INSERT INTO foo (cnt) VALUES (1), (4), (9), (16), (25), (36), (49), (64), (81), (100);
Теперь сделайте предварительно вычисленную таблицу куска суммы.Я использую переменные SQL для ясности, вы, вероятно, не нужно будет использовать те, как вы будете строить свой запрос с другого языка программирования:
SET @block = 3;
CREATE TABLE foosums (
block INTEGER PRIMARY KEY,
cntsum INTEGER
)
SELECT FLOOR((user - 1)/@block) AS block, SUM(cnt) AS cntsum
FROM foo GROUP BY block;
Теперь, чтобы вычислить сумму между @from
и @to
, вы бы захватить сумму всех полных кусков между этими двумя, и добавить все отдельные строки, которые были до кусков и после кусков. В этом примере, чтобы добавить строки 1..10, мы возьмем кусок 1 ... 3, кусок 4..6, 7..9 кусок, и отдельную строку 10.
SET @from=1, @to=10;
SELECT
COALESCE((
SELECT SUM(cnt)
FROM foo
WHERE user >= @from AND user < CEILING((@from - 1)/@block) * @block + 1
), 0)
+ COALESCE((
SELECT SUM(cntsum)
FROM foosums
WHERE block >= CEILING((@from - 1)/@block) AND block < FLOOR(@to/@block)
), 0)
+ COALESCE((
SELECT SUM(cnt)
FROM foo
WHERE user > FLOOR(@to/@block) * @block AND user <= @to
), 0)
AS blocked_total;
Чтобы проверить все работает как надо, вот unoptimised запрос, не используя порции суммы:
SELECT SUM(cnt) AS individual_total FROM foo WHERE user >= @from AND user <= @to;
И в конце концов, визуализация, чтобы помочь вам увидеть, какие именно данные охватывается оптимизированный запрос:
SELECT * FROM foo WHERE user >= @from AND user < CEILING((@from - 1)/@block) * @block + 1;
SELECT * FROM foosums WHERE block >= CEILING((@from - 1)/@block) AND block < FLOOR(@to/@block);
SELECT * FROM foo WHERE user > FLOOR(@to/@block) * @block AND user <= @to;
SQLFiddle
*) "chunk" == "block". Я написал код перед текстом и не хотел менять терминологию: p
Просто указатель на 'User'. Если это не поможет, вам может потребоваться предварительно рассчитать суммы на куски вашей базы данных. – Amadan
> Итак, у меня есть таблица, подобная этой, с более чем миллионом строк. Это не так много и не должно приводить к снижению производительности. У вас есть указатель на User-Column? Если нет, это увеличит производительность, поскольку база данных не потребуется сначала заказывать таблицу, а затем сканировать предоставленные идентификаторы пользователей. Что объясняет план выбора суммы (подсчета) из таблицы, где пользователь между x и Y говорит о том, как база данных обрабатывает ваш запрос? –
Его фактически 48 миллионов. извините за неправильную информацию – Sparsh