Python 具有多个条件的有效查询
我有一个数据库Python 具有多个条件的有效查询,python,postgresql,psycopg2,Python,Postgresql,Psycopg2,我有一个数据库 books (primary key: bookID) characterNames (foreign key: books.bookID) locations (foreign key: books.bookID) 字符名称和位置的文本位置保存在相应的表中。 现在,我想使用psycopg2编写一个Python脚本,以查找书籍中给定字符名和给定位置的所有匹配项,这两者都出现在书籍中。 目前,我执行4个查询: SELECT bookID, posit
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”元素,其中bookid不出现在“locs”中,反之亦然。我的做法是:
for cnameTuple in charnames:
~if cnameTuple[0] in locsIDs:
~~continue
~del(cname)
我对locs中的元组进行了相应的循环。不幸的是,这个算法需要很多时间。有没有办法更快地执行此任务?如果设置为
可以加快操作速度,请参见
>>> 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')])
如果设置为可加快操作速度,则可以使用该设置,请参见
>>> 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')])
通过使用联接进行查询,可以更快、更简单。
大概是这样的:
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来说,“数千本书”根本不是问题,它的设计目的是处理数百万本书。大型表的性能关键是正确的。对于此处的查询,以下索引可能会有所帮助:
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);
也许表现会更好。通过测试,它将显示使用了哪些索引以及查询的速度。创建索引非常快,使用它们进行实验很容易。只是不要保留你不需要的索引。它们也有维护费用
优化查询
我想我现在明白你在找什么了。应优化此查询,以获得每个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;
要点
- 确保基本查询只执行一次
- 仅拾取同时具有位置和名称的图书ID
- 最后一个
选择中的返回所有找到的位置。如果要修剪相同位置的重复项,请改用UNION
- 我按
bookid,position
-猜测这是需要的
- 添加了一列
what
来标记位置的来源(位置或名称)
进一步优化
如果每本书的搜索词出现次数,,您可以通过为(bookid,term)
创建带有不同项的辅助表,大大加快搜索速度。在两列上创建一个多列主索引,在术语上创建一个附加索引。为位置创建一个这样的表,为名称创建另一个这样的表。如果需要的话,用触发器让它们保持最新,但我认为书的内容变化不大。将简化和加速CTE
如果这仍然不够快,请查看。使用联接进行查询时,这可能会更快、更简单。
大概是这样的:
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来说,“数千本书”根本不是问题,它的设计目的是处理数百万本书。大型表的性能关键是正确的。对于此处的查询,以下索引可能会有所帮助:
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);
也许表现会更好。通过测试,它将显示使用了哪些索引以及查询的速度。创建索引非常快,使用它们进行实验很容易。只是不要保留你不需要的索引。它们也有维护费用
优化查询
我想我现在明白你在找什么了。应优化此查询,以获得每个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;
要点
- 确保基本查询只执行一次
- 仅拾取同时具有位置和名称的图书ID
- 最后一个
选择中的返回所有找到的位置。如果要修剪相同位置的重复项,请改用UNION
- 我按
bookid,position
-猜测这是需要的
- 添加了一列
what
来标记位置的来源(位置或名称)
进一步优化
如果每本书的搜索词出现次数,,您可以通过为(bookid,term)
创建带有不同项的辅助表,大大加快搜索速度。在两列上创建一个多列主索引,在术语上创建一个附加索引。为位置创建一个这样的表,为名称创建另一个这样的表。如果需要的话,用触发器让它们保持最新,但我认为书的内容变化不大。将简化和加速CTE
如果这仍然不够快,请查看。您好,很抱歉我还没有回复。使用JOIN是我的第一种方法,但有人告诉我这太昂贵了。我正在研究一个由数千本书组成的语料库,所以交叉连接需要时间。(我可能错了,但你的解决方案是一种带有附加条件的交叉连接。如果我错了,请随时纠正我。)我同意这一点,但是否有可能只获得一次所有位置