如何以更快的方式计算1:n:n关系中的行数?

ryevplcw  于 2021-06-19  发布在  Mysql
关注(0)|答案(2)|浏览(351)

这个问题对我来说有点复杂,我不能用一句话来解释,所以题目可能显得很模棱两可。
我的mysql数据库中有3个表,它们的结构如下:
单词列表(500万行)

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

纸字关系(1000万行)

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

论文引用关系(8万行)

+----------+--------+
    | 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 table(大约400万字(只出现一次)
谢谢!

jhdbpxl9

jhdbpxl91#

如果您只关心获取计数,则不应首先将结果获取到派生表中,然后再将行计数出去。这可能会创建不必要的临时表,在内存中存储大量数据。您可以直接计算行数。
我还认为你需要计算论文的独特数量。因为很多人之间的关系 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 table。
综合指数 (pid, word)paper_word_relation table。
我们还可以通过减少一个连接和使用条件连接来进一步优化查询 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)
     )
toiithl6

toiithl62#

第一次之后 1:n 加入你也一样 pid_to 多次,你的下一次加入不再是 1:n 但是 n:m ,在决赛前创造了一个巨大的中间结果 DISTINCT . 它类似于交叉连接,对于流行词来说情况越来越糟,例如1010对10001000行。
必须在连接之前删除重复项,这将返回与@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

这个返回的数字可能略高一些(不管引用的是什么论文,它都会计算在内)。

相关问题