SQL подсчитывает значения многие-ко-многим или он учитывается при каждом добавлении новой строки? - PullRequest
3 голосов
/ 27 января 2010

Я использую MySQL (MyISAM) 5.0.41, и у меня есть этот запрос:

SELECT `x`.`items`.id, `x`.`items`.name, COUNT(*) AS count
    FROM `x`.`items` INNER JOIN `x`.`user_items`
    ON `x`.`items`.id = `x`.`user_items`.item_id
    GROUP BY name HAVING count > 2 ORDER BY count DESC

У меня около 36 000 пользователей, 175 000 user_items и 60 000 элементов, которые постоянно добавляются. Так что этот запрос становится немного медленным ...

Лучше ли:

  • Иметь поле count в items и периодически обновлять его (например, каждый раз, когда пользователь добавляет элемент)
  • или выполните запрос вот так (медленно) ..

Или есть какой-нибудь SQL, который заполнит поле подсчета для меня?

Спасибо

Ответы [ 5 ]

3 голосов
/ 27 января 2010

Можно использовать промежуточный раствор:

  • Добавить столбец ts DATETIME в таблицу user_items, который будет описывать время, когда пользователь добавил элемент

  • Добавить столбец ts DATETIME в таблицу users, который будет описывать точку действительности, пока cnt, столбец кэшированного подсчета

  • Периодически обновляйте таблицу users, добавляя новый счет и отметку времени:

    INSERT
    INTO    users (id, ts, cnt)
    SELECT  *
    FROM    (
            SELECT  user_id, NOW() AS nts, COUNT(*) AS ncnt
            FROM    user_items ui
            WHERE   ui.timestamp <= NOW()
            )
    ON DUPLICATE KEY
    UPDATE  ts = nnow,
            cnt = ncnt
    
  • Недействительная метка времени пользователя при удалении записи user_items

  • Выполните этот запрос для подсчета элементов:

    SELECT  u.id, u.cnt +
            (
            SELECT  COUNT(*)
            FROM    user_items ui
            WHERE   ui.ts > u.ts
                    AND ui.user_id = u.id
            )
    FROM    users
    

Таким образом, только добавленные элементы будут учитываться в таблице user_items, что намного быстрее, и у вас не будет проблем с параллелизмом при слишком частом обновлении записей.

2 голосов
/ 27 января 2010

Вы должны начать с индексации user_items.item_id и группировки по ней вместо имени. Строки группируются намного медленнее (попробуйте сами), и индекс должен немного ускорить процесс. Если это все еще слишком медленно, вы можете сначала выполнить запрос GROUP BY, а затем присоединиться к таблице элементов, если ваш план выполнения СУБД не делает этого по умолчанию.

1 голос
/ 27 января 2010

Этот запрос каждый раз выполняет полное сканирование таблицы. Обойти это невозможно. Индексы ускорят мое ускорение объединения, но запрос будет становиться все медленнее и медленнее по мере роста ваших данных.

Хранение итоговых данных, таких как "count" с "items", было бы правильным способом. Вы можете сделать это с помощью хранимых процедур или с помощью кода. В качестве двойной проверки вы можете периодически (т.е. один раз в день) обновлять все подсчеты, чтобы вы знали, что они точны.

0 голосов
/ 27 января 2010

Действительно ли вы получаете всех 36 000 пользователей каждый раз, когда запускаете запрос? Если вы ищете источник проблемы с производительностью, то это может быть прямо здесь.

В зависимости от вашей РСУБД вы можете смотреть на такие вещи, как индексированные или материализованные представления. Включение счетчика в таблицу и попытка его сохранить почти наверняка будет ошибкой, особенно с учетом небольшого размера вашей базы данных.

0 голосов
/ 27 января 2010

Мой импульс состоит в том, чтобы оставить данные в чем-то похожем на нормальную форму (другими словами, не увеличивать поле "count"), а затем кэшировать результат медленного запроса на уровне приложения .

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...