есть ли что-то более быстрое, чем подсчет для больших таблиц?

Вот мой запрос:

select word_id, count(sentence_id) 
from sentence_word 
group by word_id 
having count(sentence_id) > 100;

Табличное предложение содержит 3 поля: слово, предложение и идентификатор первичного ключа. Он имеет 350k+ строк. Этот запрос занимает колоссальные 85 секунд, и мне интересно (надеюсь, молюсь?), есть ли более быстрый способ найти все идентификаторы слов, которые содержат более 100 идентификаторов предложений.

Я попытался убрать часть выбора счетчика и просто сделать «имея счет (1)», но ни один из них не ускоряет его.

Буду признателен за любую помощь, которую вы можете оказать. Спасибо!


person Jeff    schedule 04.05.2009    source источник
comment
Это с MySQL (и с использованием HeidiSQL в качестве клиента для доступа к нему)   -  person Jeff    schedule 05.05.2009
comment
Еще одно досадное уточнение... (извините): данные постоянно меняются. Около 10 тыс. вставленных строк в день и ~ 5 тыс. удаленных строк. Поэтому я думаю, что это делает невозможным сохранение или кеширование результатов.   -  person Jeff    schedule 05.05.2009


Ответы (5)


количество (идентификатор_предложения) > 100;

С этим есть проблема... Либо в таблице есть повторяющиеся пары слово/предложение, либо нет.

Если в нем есть повторяющиеся пары слово/предложение, вы должны использовать этот код, чтобы получить правильный ответ:

HAVING COUNT(DISTINCT Sentence_ID) > 100

Если в таблице нет повторяющихся пар слово/предложение... тогда вы не должны считать id_предложений, вы должны просто считать строки.

HAVING COUNT(*) > 100

В этом случае вы можете создать индекс только для word_id для оптимальной производительности.

person Amy B    schedule 04.05.2009
comment
Удивительно, как такая простая вещь, как *, является разницей между 85-секундным запросом и буквально 0,3 секунды! Спасибо всем- - person Jeff; 05.05.2009

Если у вас его еще нет, создайте составной индекс для id_предложения, id_слова.

person Mitch Wheat    schedule 04.05.2009
comment
Я считаю, что правильный порядок столбцов для этого индекса будет (word_id, предложение_id). - person Irina C; 04.05.2009
comment
@Irina C: Я только что проверил на SQL-сервере столбцы индекса в обоих направлениях, и, несмотря на разные планы выполнения, количество логических операций ввода-вывода было одинаковым. - person Mitch Wheat; 04.05.2009

Если этот запрос выполняется часто, а таблица редко обновляется, вы можете сохранить вспомогательную таблицу с идентификаторами слов и соответствующими счетчиками предложений — трудно придумать какую-либо дальнейшую оптимизацию помимо этого!

person Alex Martelli    schedule 04.05.2009

Ваш запрос в порядке, но для получения более быстрых результатов требуется небольшая помощь (индексы).

У меня нет своих ресурсов под рукой (или доступа к SQL), но я попытаюсь помочь вам по памяти.

Концептуально единственный способ ответить на этот запрос — подсчитать все записи, которые имеют один и тот же word_id. Это означает, что механизму запросов нужен быстрый способ найти эти записи. Без индекса по word_id единственное, что может сделать база данных, — это просмотреть таблицу по одной записи за раз и продолжать подсчитывать итоги для каждого отдельного найденного слова word_id. Обычно для этого требуется временная таблица, и никакие результаты не могут быть отправлены, пока вся таблица не будет просканирована. Нехорошо.

С индексом для word_id ему все равно придется проходить через таблицу, так что можно подумать, это мало поможет. Однако механизм SQL теперь может вычислять количество для каждого word_id, не дожидаясь конца таблицы: он может отправить строку и количество для этого значения word_id (если оно соответствует вашему предложению where) или отбросить строку (если это не так); это приведет к меньшей нагрузке на память на сервере, возможно, частичным ответам, и временная таблица больше не понадобится. Второй аспект — параллелизм; с индексом word_id SQL может разделить задание на части и использовать отдельные ядра процессора для параллельного выполнения запроса (в зависимости от возможностей оборудования и существующей рабочей нагрузки).

Этого может быть достаточно, чтобы помочь вашему запросу; но вам придется попытаться увидеть:

CREATE INDEX someindexname ON sentence_word (word_id)

(Синтаксис T-SQL; вы не указали, какой продукт SQL вы используете)

Если этого недостаточно (или совсем не помогает), есть два других решения.

Во-первых, SQL позволяет предварительно вычислить COUNT(*) с помощью индексированных представлений и других механизмов. Деталей под рукой нет (да и делаю я это нечасто). Если ваши данные не меняются часто, это даст вам более быстрые результаты, но с затратами на сложность и немного места для хранения.

Кроме того, вы можете захотеть сохранить результаты запроса в отдельной таблице. Это практично только в том случае, если данные никогда не меняются или изменяются по точному графику (скажем, во время обновления данных в 2 часа ночи), или если они меняются очень мало и вы можете жить с неидеальными результатами в течение нескольких часов (вы придется запланировать периодическое обновление данных); это моральный эквивалент хранилища данных бедняка.

Лучший способ узнать наверняка, что работает для вас, — запустить запрос и посмотреть план запроса с некоторыми индексами-кандидатами, такими как приведенный выше, и без них.

person Euro Micelli    schedule 04.05.2009
comment
Спасибо за полезный ответ! На самом деле, все соответствующие поля уже проиндексированы... Но простое изменение запроса на count(*) вместо count(sentence_id) было ответом!! Ночь и день. Так что, похоже, это так. Я думаю, по какой-то причине я предположил, что count() с определенным полем более эффективен, чем с *, но теперь это кажется глупым предположением. Сделаем еще несколько проверок, чтобы подтвердить, что проблема именно в этом. - person Jeff; 05.05.2009

Удивительно, но для больших наборов данных это можно сделать еще быстрее:

SELECT totals.word_id, totals.num 
  FROM (SELECT word_id, COUNT(*) AS num FROM sentence_word GROUP BY word_id) AS totals
 WHERE num > 1000;
person Max Kanat-Alexander    schedule 05.10.2010