我有一个巨大的索引(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.
intersect
似乎表现良好: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);
bool_or()
检查页面上的任何单词是否是您的第一个单词,和检查另一个单词是否是您的第一个单词。