【问题标题】:postgreSQL nested query performing slowpostgreSQL 嵌套查询执行缓慢
【发布时间】:2013-11-06 14:38:20
【问题描述】:

我有这三个表:

  1. 创建表格单词(id整数、单词文本、频率整数);
  2. 创建表格句子(id整数,句子文本);
  3. 创建表索引(wordId整数、sentenceId整数、位置整数);

Index 是倒排索引,表示哪个单词出现在哪个句子中。此外,我有一个来自表格单词和句子的 id 索引。

此查询确定给定单词出现在哪些句子中并返回第一个匹配项:

select S.sentence from sentences S, words W, index I
where W.word = '#erhoehungen' and W.id = I.wordId and S.id = I.sentenceId
limit 1;

但是当我想检索两个单词一起出现的句子时:

select S.sentence from sentences S, words W, index I
where W.word = '#dreikampf' and I.wordId = W.id and S.id = I.sentenceId and
S.id in (
    select S.id from sentences S, words W, index I
    where W.word = 'bruederle' and W.id = I.wordId and S.id = I.sentenceId
)
limit 1;

这个查询要慢得多。有什么技巧可以加快速度吗?以下是我到目前为止所做的事情:

  • 将 shared_buffer 增加到 32MB
  • work_mem 增加到 15MB
  • 对所有表运行分析
  • 如前所述,在单词 id 和句子 id 上创建索引

问候。

€编辑:

这里是解释分析查询语句的输出:http://pastebin.com/t2M5w4na

这三个create语句其实是我原来的create语句。我应该在表格句子和单词中添加主键并将它们作为索引中的外键引用吗?但是我应该为索引表使用什么主键? SentId 和 wordId 一起不是唯一的,即使我添加 pos 表示单词在句子中的位置,它也不是唯一的。

更新为:

  1. 创建表单词(id整数、单词文本、频率整数、主键(id));
  2. 创建表格句子(id整数、句子文本、主键(id));
  3. 创建表索引(wordId整数,sentenceId整数,位置整数,外键(wordId)引用词(id),外键(sentenceId)引用句子(sentenceId));

【问题讨论】:

  • 编辑您的问题,并粘贴explain analyze your_query 的输出,其中“your_query”代表您麻烦的 SELECT 语句。此外,实际的 CREATE TABLE 语句也有很大帮助。
  • 你的表index(可怕的名字,顺便说一句)至少需要一个主键。 {sentenceid, position} 是显而易见的选择。在{sentenceid,wordid} 和/或{wordid,sentenceid} 上设置一个或两个复合索引可能也会有所帮助。
  • 另外:你需要一个 UNIQUE 约束或索引表的 natural 键:word 本身。记录外:RDBMS 和 nlp 不匹配。您可以查看其他存储方法(对于 Postgres:hstore,或用于全文搜索的 GIST 索引)
  • 键值对{sentenceid, position}不是唯一的,因为有些句子是重复的。感谢您提供有关其他存储方法的信息。
  • 为什么要允许重复的句子?没有额外的(关键)列,重复的句子是没有意义的。

标签: database postgresql relational-database


【解决方案1】:

我想这应该更有效:

SELECT s.id, s.sentence FROM words w
JOIN INDEX i ON w.id = i.wordId
JOIN sentences s ON i.sentenceId = s.id
WHERE w.word IN ('#dreikampf', 'bruederle')
GROUP BY s.id, s.sentence
HAVING COUNT(*) >= 2

只需确保IN 子句中的项目数量与HAVING 子句中的项目数量相匹配。

小提琴here.

【讨论】:

  • 另外,如果你想添加更多的单词,你不需要在这个解决方案中添加更多的SQL代码,而是改变参数:)
  • 非常感谢。它比我的解决方案快得多,但仍以秒为单位。也许是因为表的大小:单词(255715 行)、句子(5085623 行)和索引(61029790 行)。
  • 61 毫米?这是一个很大的数字:) 我猜下一个级别的性能将是在索引上工作。但也许你应该在Database Administrators 中问这个问题。
  • 感谢您的链接。也许我也可以尝试使用 MyISAM 作为存储引擎的 mysql,因为它使用的安全性比 postgreSQL 少——但我没有这方面的经验。
【解决方案2】:

您的列wordIdsentenceId 上似乎没有索引。请创建它们,查询会更快。

CREATE INDEX idx_index_wordId ON index USING btree (wordId);
CREATE INDEX idx_index_sentenceId ON index USING btree (sentenceId);

使用保留字index 作为表名不是一个好主意——在某些情况下您可能需要对其进行转义。 可能您还应该将列 id 添加到 index 表并使其成为主键。

请使用Mosty Mostacho 查询并在创建索引后显示它的explain analyze 输出。也许它可以工作得更快。

更新:

请尝试新查询:

select S.sentence from sentences S where S.id in
(select sentenceId from index I where 
I.wordId in (select id from words where word IN ('#dreikampf', 'bruederle'))
group by I.sentenceId
having count(distinct I.wordId) = 2
limit 1)

【讨论】:

  • 为两个 id 添加索引并将索引表重命名为 inv_w。这是解释分析的输出:pastebin.com/veVds6KP 仍以秒为单位。我只对第一个/一个匹配感兴趣,所以也许我可以使用光标?因为此查询检索所有解决方案。
  • 请同时创建此索引:CREATE INDEX idx_words_word ON words USING btree (word); 并将LIMIT 1 添加到查询的末尾以仅获取一行。
  • 我也更新了我的答案——请尝试新的查询。它应该更快、更正确地工作(处理一个句子中有 2 个相同单词的情况)。
  • 带有“#dreikampf”和“bruederle”的查询需要大约 900 毫秒。但是使用“bruederle”和“punto-vergleich”这两个词需要 12629 毫秒:/
  • 请再创建一个索引CREATE INDEX idx_index_wordId_sentenceId ON index USING btree (wordId, sentenceId);。出于某种原因,Postgres 选择了错误的计划,这应该会有所帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-23
  • 2011-09-10
  • 2020-04-23
  • 2018-07-19
  • 1970-01-01
  • 2019-07-27
  • 1970-01-01
相关资源
最近更新 更多