我有一个数据库
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'
这两个查询都可以给我 bookIDs,其中只显示名称或位置。因此,我的目标是消除“charnames”的所有元素,其中 bookID 未出现在“locs”中,反之亦然。我的方法是:
for cnameTuple in charnames:
~if cnameTuple[0] in locsIDs:
~~continue
~del(cname)
我为locs中的元组做了一个相应的循环。
不幸的是,这个算法需要很多时间。有没有办法更快地执行此任务?
最佳答案
如果使用 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';
only retrieve the data you need from the database server 通常是个好主意而不是在您的应用程序中获取过多的数据和过滤。数据库引擎针对过滤和排序进行了优化。你很难在你的应用程序中匹配它。
请注意我是如何使用所有小写名称的。阅读 manual about identifiers .
评论后的更多信息
对于像 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
。 UNION ALL
在最后的SELECT
中返回所有 找到的位置。如果您想修剪具有相同位置的重复项,请改用UNION
。- 我按
bookid, position
排序 - 猜想这是需要的。 - 添加了一列
what
来标记位置的来源(位置或名称)。
进一步优化
如果搜索字词在每本书中出现多次,您可以通过为(bookid, term)创建具有不同条目的辅助表来大大加快搜索速度
。在两列上创建一个多列主索引,并在 term
上创建一个附加索引。为位置创建一个这样的表,为名称创建另一个表。如果需要的话,让它们与触发器保持同步,但我认为书籍的内容变化不大。将简化和加速 CTE。
如果仍然不够快,请查看 Full Text Search .
关于python - 多条件有效查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10036645/