0

私はデータベースを持っています

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 を取得できます。したがって、私の目標は、「locs」で発生しない bookID を持つ「charnames」のすべての要素とその逆を排除することです。私のアプローチは次のとおりです。

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

locs のタプルに対応するループを作成しました。
残念ながら、このアルゴリズムには多くの時間が必要です。このタスクをより速く実行する方法はありますか?

4

2 に答える 2

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';

コメント後の詳細

「何千冊もの本」は、何百万もの. 大きなテーブルでのパフォーマンスの鍵は、適切なインデックスです。ここでのクエリでは、次のインデックスが役立つ可能性があります。

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);

複数列のインデックスは、さらに優れたパフォーマンスを発揮する場合があります。でテストすると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クエリ)は、基本クエリが 1 回だけ実行されるようにします。
  • INTERSECTbookids場所名前の両方を特徴とするものだけを選びます。
  • UNION ALL最後のは、見つかったすべてSELECTの位置を返します。同じ位置で重複をトリミングする場合は、代わりに使用します。UNION
  • 私は注文しbookid, positionます-それが必要なものだと思います。
  • what位置の情報源 (場所または名前) にタグを付けるための列を追加しました。

さらなる最適化

書籍ごとに検索用語が何度も表示される場合は、. 2 つの列に複数列のプライマリ インデックスを作成し、1 つだけに追加のインデックスを作成します。そのようなテーブルを場所用に 1 つ作成し、名前用に別のテーブルを作成します。必要に応じてトリガーを付けて最新の状態に保ちますが、本の内容はあまり変わっていないと思います。CTEを簡素化し、高速化します。(bookid, term)term

それでも不十分な場合は、全文検索を調べてください。

于 2012-04-05T23:36:40.597 に答える
0

see操作を高速化する場合は、 set to を使用できます

>>> 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')])
于 2012-04-05T22:42:37.583 に答える