Как я могу посчитать строки в соотношении 1: N: N более быстрым способом? - PullRequest
0 голосов
/ 20 ноября 2018

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

У меня есть 3 таблицы в базе данных MySQL, их структура показана ниже:

  1. список слов (5 миллионов строк)

    +-----+--------+
    | wid | word   |
    +-----+--------+
    |   1 | foo    |
    |   2 | bar    |
    |   3 | hello  |
    +-----+--------+

paper_word_relation (10 миллионов строк)

    +-----+-------+
    | pid | word  | 
    +-----+-------+
    |   1 |    1  | 
    |   1 |    2  | 
    |   1 |    3  | 
    |   2 |    1  | 
    |   2 |    3  | 
    +-----+-------+

paper_citation_relation (80K строк)

    +----------+--------+
    | pid_from | pid_to | 
    +----------+--------+
    |        1 |     2  | 
    |        1 |     3  | 
    |        1 |     4  |
    |        2 |     1  |
    |        2 |     3  |
    +----------+--------+

Я хочу узнать, сколько статей содержит слово W, и процитировать статьи также содержат слово W. (для каждого слова в списке)

Я использую два внутренних соединения, чтобы выполнить эту работу, но оно кажется очень медленным, когда слово популярно - выше 50 с (довольно быстро, если слово используется редко - ниже 0,1 с), вот мой код


    SELECT COUNT(*) FROM (
    SELECT a.pid_from, a.pid_to, b.word FROM paper_citation_relation AS a 
    INNER JOIN paper_word_relation AS b ON a.pid_from = b.pid
    INNER JOIN paper_word_relation AS c ON a.pid_to = c.pid
    WHERE b.word = 2 AND c.word = 2) AS d

Как я могу сделать это быстрее?Мой запрос недостаточно эффективен или проблема с объемом данных?

Я могу предложить только одно решение: я удаляю слова, встречающиеся меньше 2 в таблице paper_word_relation.(Около 4 миллионов слов встречаются только один раз)

Спасибо!

Ответы [ 2 ]

0 голосов
/ 20 ноября 2018

После первого 1:n объединения вы получаете одно и то же pid_to несколько раз, и ваше следующее объединение больше не 1:n, а n:m, создавая, возможно, огромный промежуточный результат перед финальным DISTINCT.Это похоже на CROSS JOIN и ухудшается для популярных слов, например, 10 * 10 против 1000 * 1000 строк.

Вы должны удалить дубликаты перед объединением, это должно вернуть то же число, что и ответ @ MadhurBhaiya

SELECT Count(*) -- no more DISTINCT needed
FROM 
 (
    SELECT DISTINCT cr.pid_to -- reducing m to 1
    FROM paper_citation_relation AS cr
    JOIN paper_word_relation AS wr 
      ON cr.pid_from = wr.pid
    WHERE wr.word = 2
 ) AS dt
JOIN paper_word_relation AS wr
  ON dt.pid_to = wr.pid  -- 1:n join again
WHERE wr.word = 2

Если вы хотите сосчитать количество цитируемых статей, вам нужно сначала получить отдельный список pid (pid_from или pid_to) из paper_citation_relation, а затемприсоединиться к определенному слову.

SELECT Count(*)
FROM
( -- get a unique list of cited or citing papers
    SELECT pid_from AS pid -- citing
    FROM paper_citation_relation
    UNION -- DISTINCT by default
    SELECT pid_to          -- cited
    FROM paper_citation_relation 
) AS dt
JOIN paper_word_relation AS wr
  ON wr.pid = dt.pid
WHERE wr.word = 2 -- now check for the searched word

Число, возвращаемое этим, может быть немного выше (оно считает бумагу независимо от того, цитируется она или цитируется).

0 голосов
/ 20 ноября 2018

Если вас интересует только получение количества, вам не следует сначала получать результаты в производную таблицу, а затем считать строки. Это может создавать ненужные временные таблицы, хранящие большое количество данных в памяти. Вы можете напрямую посчитать количество строк.

Я также думаю, что вам нужно посчитать уникальное количество бумаг. Из-за связи «многие ко многим» в таблице paper_citation_relation дубликаты строк могут появляться для одной бумаги.

SELECT COUNT(DISTINCT a.pid_from) 
FROM paper_citation_relation AS a 
INNER JOIN paper_word_relation AS b ON a.pid_from = b.pid
INNER JOIN paper_word_relation AS c ON a.pid_to = c.pid
WHERE b.word = 2 AND c.word = 2

Для производительности вам понадобится следующее индексирование:

  • Составной индекс на (pid_from, pid_to) в таблице paper_citation_relation.
  • Составной индекс по (pid, word) в таблице paper_word_relation.

Мы можем также, возможно, еще больше оптимизируем запрос за счет сокращения одного объединения и использования условной фильтрации AND/OR в HAVING. Вам нужно будет сравнить его.

SELECT COUNT(*) 
FROM (
      SELECT a.pid_from  
      FROM paper_citation_relation AS a 
      INNER JOIN paper_word_relation AS b 
        ON (a.pid_from = b.pid OR 
            a.pid_to = b.pid)  
      GROUP BY a.pid_from 
      HAVING SUM(a.pid_from = b.pid AND b.word = 2) AND 
             SUM(a.pid_to = b.pid AND b.word = 2)
     )

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