查找大型数据集的两个子部分之间的 SQL 交集的最快方法?

问题描述 投票:0回答:1

我有一个巨大的索引(1.04 亿行)文档、页面和单词表:

CREATE TABLE my_table (
    doc_id integer,
    page_id integer,
    word_id integer
);

CREATE INDEX my_table_word ON my_table (word_id);
CREATE INDEX my_table_doc ON my_table (doc_id);
CREATE INDEX my_table_page my_table (page_id);

我想在同一文档中查找同时包含单词 A 和单词 B 的页面。我当前的尝试如下:

尝试 1 - 聚合事物:

SELECT doc_id, page_id
FROM my_table
WHERE word_id in (123, 456)
group by 1,2 
having count(distinct word_id) = 2

-- ~39k row result, took 20 seconds

尝试 2) 使用 CTE,速度稍快

with foo as (
    select doc_id, page_id
    from my_table
    where word_id = 123 -- foo -- 44k rows
),

bar as (
    select doc_id, page_id
    from my_table
    where word_id = 456 -- bar -- 439k rows
)

select f.doc_id, f.page_id
from foo f
inner join bar b on f.doc_id = b.doc_id and f.page_id = b.page_id

-- same results, takes 15 seconds

尝试 3) - 在两个 CTE 之间执行

INTERSECT
的 15 秒完全相同,可能是相同的查询计划。

有没有更快的方法来做到这一点?我希望能把这个降到< 1 second for a web app with somewhat impatient users.

postgresql indexing query-optimization
1个回答
0
投票

基本的

intersect
似乎表现良好:
db<>fiddle 的演示

select doc_id, page_id
from my_table
where word_id = 456
INTERSECT
select doc_id, page_id
from my_table
where word_id = 123;

在我对 700k 文档(每页 100 页,使用 1k 个单词)的测试中,它的运行速度与第二个示例一样快,同时更简单且无重复,因为它默认为

INTERSECT DISTINCT

您还可以使用

exists
:

explain analyze verbose
select distinct doc_id, page_id
from my_table as a
where word_id = 456
  and exists(select from my_table as b 
             where a.doc_id=b.doc_id
               and a.page_id=b.page_id
               and word_id = 123);

运行速度也差不多。

不过,您尝试使用聚合和

having
在一次传递、一次查询中找到所需的所有内容是正确的:

select doc_id, page_id
from my_table
group by doc_id, page_id
having bool_or(word_id=456) 
   and bool_or(word_id=123);

我的设置速度快了 8 倍。两个

bool_or()
检查页面上的任何单词是否是您的第一个单词,检查另一个单词是否是您的第一个单词。

© www.soinside.com 2019 - 2024. All rights reserved.