有效查询多个条件

时间:2012-04-05 22:03:02

标签: python postgresql psycopg2

我有一个

的数据库
books          (primary key: bookID)
characterNames (foreign key: books.bookID) 
locations      (foreign key: books.bookID)

字符名称和位置的文本位置保存在相应的表格中 现在我想用psycopg2编写一个Python脚本来查找给定字符名称和书籍中给定位置的所有出现位置。 目前,我执行了4个查询:

SELECT bookID, position FROM characterNames WHERE name='XXX';
--> result is saved in list 'charnames'

SELECT DISTINCT bookID FROM characterNames WHERE name='XXX';
--> result is saved in list 'charnamesIDs'

SELECT bookID, position FROM locations WHERE locName='YYY';
--> result is saved in list 'locs'

SELECT bookID FROM locations WHERE locName='YYY';
--> result is saved in list 'locsIDs'

这两个查询都可以为我提供bookID,其中只显示名称或位置。所以我的目标是消除'charnames'的所有元素,其中bookIDs不会出现在'locs'中,反之亦然。我的方法是:

for cnameTuple in charnames:  
~if cnameTuple[0] in locsIDs:  
~~continue  
~del(cname)

我为locs中的元组做了相应的循环 遗憾的是,该算法需要大量时间。有没有办法更快地执行此任务?

2 个答案:

答案 0 :(得分:3)

使用JOIN查询可以更快更简单 像这样:

SELECT b.*, c.position, l.position
FROM   books b
JOIN   characternames c USING (bookid)
JOIN   locations l USING (bookid)
WHERE  c.name = 'XXX'
AND    l.locname = 'YYY';

评论后的更多信息

对于像PostgreSQL这样设计用于处理数百万的RDBMS,“成千上万本书”完全没问题。大表的性能关键是indexes。对于此处的查询,以下索引可能会有所帮助:

CREATE INDEX books_bookid_idx ON books(bookid); -- a primary key will do, too

CREATE INDEX cn_bookid_idx ON characternames (bookid);
CREATE INDEX cn_name_idx ON characternames (name);

CREATE INDEX locations_bookid_idx ON locations (bookid);
CREATE INDEX locations_locname_idx ON locations (locname);

Multicolumn indexes可能表现得更好。使用EXPLAIN ANALYZE进行测试,它将显示使用哪些索引以及查询的速度。创建索引非常快,尝试使用它们很容易。只是不要保留你不需要的索引。它们也需要维护费用。


优化查询

think我现在明白了,你在寻找什么。应优化此查询以获取每个bookid的位置或名称的所有位置,但仅限于名称​​和位置显示在同一本书中的位置,并且每本书没有进一步的详细信息:

WITH b AS (
    SELECT bookid
    FROM   characternames
    WHERE  name = 'XXX'
    GROUP  BY 1
    INTERSECT
    SELECT bookid
    FROM   locations
    WHERE  l.locname = 'YYY'
    GROUP  BY 1
    )
SELECT bookid, position, 'char' AS what
FROM   b
JOIN   characternames USING (bookid)
WHERE  name = 'XXX'
UNION  ALL
SELECT bookid, position, 'loc' AS what
FROM   b
JOIN   locations USING (bookid)
WHERE  locname = 'YYY'
ORDER  BY bookid, position;

重点

  • CTE (WITH query)确保基本查询仅执行一次。
  • INTERSECT仅挑选具有位置的bookids
  • 最终SELECT中的UNION ALL会返回找到的所有位置。如果您想修剪具有相同位置的重复项,请使用UNION
  • 我按bookid, position命令 - 猜测这就是所需要的。
  • 添加了一列what来标记某个职位的来源(位置或名称)。

进一步优化

如果每本书的搜索字词多次,您可以通过为(bookid, term)创建包含不同条目的辅助表来大大加快搜索速度。在两列上创建多列主索引,在term上创建另一列。为位置创建一个这样的表,为名称创建另一个表。如果需要的话,让它们与触发器保持同步,但我认为书籍的内容没有太大变化。将简化和加快CTE。

如果仍然不够快,请查看Full Text Search

答案 1 :(得分:0)

如果加速操作,您可以使用设置为see

>>> xxx = set([(1,'a'), (2,'b')])
>>> xxx
set([(1, 'a'), (2, 'b')])
>>> xxx = set([(1,'a'), (3,'c')])
>>> yyy
set([(1, 'a'), (3, 'c')])
>>> c = xxx.intersection(yyy)
>>> c
set([(1, 'a')])   # common between xxx and yyy
>>> xxx - c
set([(2, 'b')])