SQL Альтернатива выполнению INNER JOIN для одной таблицы - PullRequest
5 голосов
/ 08 августа 2009

У меня есть большая таблица (TokenFrequency), в которой миллионы строк. Таблица TokenFrequency имеет такую ​​структуру:

Таблица - TokenFrequency

  • id - int, первичный ключ
  • источник - int, внешний ключ
  • токен - символ
  • count - int

Моя цель - выбрать все строки, в которых два источника имеют одинаковый токен. Например, если мой стол выглядел так:

id --- источник --- токен --- count
1 ------ 1 --------- собака ------- 1
2 ------ 2 --------- кошка -------- 2
3 ------ 3 --------- кошка -------- 2
4 ------ 4 --------- свинья -------- 5
5 ------ 5 --------- зоопарк ------- 1
6 ------ 5 --------- кошка -------- 1
7 ------ 5 --------- свинья -------- 1

Я бы хотел, чтобы запрос SQL дал мне источник 1, источник 2 и сумму подсчетов. Например:

source1 --- source2 --- token --- count
---- 2 ----------- 3 --------- кошка -------- 4
---- 2 ----------- 5 --------- кошка -------- 3
---- 3 ----------- 5 --------- кошка -------- 3
---- 4 ----------- 5 --------- свинья -------- 6

У меня есть запрос, который выглядит так:

SELECT  F.source AS source1, S.source AS source2, F.token, 
       (F.count + S.count) AS sum 
FROM       TokenFrequency F 
INNER JOIN TokenFrequency S ON F.token = S.token 
WHERE F.source <> S.source

Этот запрос работает нормально, но у меня есть следующие проблемы:

  1. У меня есть таблица TokenFrequency, в которой миллионы строк, и поэтому мне нужен более быстрый вариант для получения этого результата.
  2. Текущий мой запрос дает дубликаты. Например, его выбор:
    source1 = 2, source2 = 3, токен = cat, количество = 4
    source1 = 3, source2 = 2, токен = cat, количество = 4
    Это не слишком большая проблема, но если есть способ улучшить их и, в свою очередь, добиться увеличения скорости, это было бы очень полезно

Основная проблема, с которой я сталкиваюсь, - это скорость выполнения запроса. Мой текущий запрос занимает несколько часов. ВНУТРЕННЕЕ СОЕДИНЕНИЕ на столе к себе - вот что я считаю проблемой. Я уверен, что должен быть способ устранить внутреннее соединение и получить аналогичные результаты, просто используя один экземпляр таблицы TokenFrequency. Вторая проблема, о которой я упоминал, также может способствовать увеличению скорости запроса.

Мне нужен способ реструктуризации этого запроса, чтобы быстрее и эффективнее получать те же результаты.

Спасибо.

Ответы [ 3 ]

2 голосов
/ 08 августа 2009

Попробуйте это:

SELECT token, GROUP_CONCAT(source), SUM(count)
FROM TokenFrequency
GROUP BY token;

Это должно работать намного быстрее, а также устранять дубликаты. Но источники будут возвращены в списке через запятую, поэтому вам придется взорвать его в своем приложении.

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


обновление: Кажется, я неправильно понял ваш вопрос. Вам не нужна сумма подсчетов для каждого токена, вам нужна сумма отсчетов для каждой пары источников для данного токена.

Я считаю, что внутреннее соединение - лучшее решение для этого. Важное указание для SQL заключается в том, что если вам нужно вычислить выражение для двух разных строк, вам нужно выполнить соединение.

Однако один из методов оптимизации, о котором я упоминал выше, - это использование охватывающего индекса , чтобы все необходимые столбцы были включены в структуру данных индекса. Преимущество состоит в том, что все ваши поиски - O (log n), и запросу не нужно делать второй ввод-вывод для чтения физической строки, чтобы получить другие столбцы.

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

2 голосов
/ 08 августа 2009

Мне нужно немного больше информации, чтобы диагностировать проблему со скоростью, но чтобы убрать дупс, добавьте это в ГДЕ:

AND F.source<S.source
1 голос
/ 08 августа 2009

Если токен не проиндексирован, он обязательно должен быть.

...