6

FTS4 モジュールを使用して生成された SQLite テーブルがあります。各エントリは、異なる言語で少なくとも 2 回リストされていますが、一意の ID を共有しています (int 列、インデックス化されていません)。これが私がやりたいことです: 優先言語で用語を検索したい. 結果を、別の言語を使用して同じ用語のルックアップと結合したいと考えています。ただし、2 回目の検索では、最初の検索で既に見つかった (ID で識別される) すべてのエントリを無視したいと考えています。だから基本的に私はこれをしたい:

WITH term_search1 AS (
    SELECT *
    FROM myFts
    WHERE myFts MATCH 'term'
    AND languageId = 1)
SELECT *
FROM term_search1
UNION
SELECT *
FROM myFts
WHERE myFts MATCH 'term'
AND languageId = 2
AND id NOT IN (SELECT id FROM term_search1)

ここでの問題は、term_seach1 クエリが 2 回実行されることです。私の結果を具体化する方法はありますか?(3 ではなく) 2 つのクエリに制限するための解決策は素晴らしいでしょう。

また、次のような再帰クエリを使用してみました。

WITH RECURSIVE term_search1 AS (
    SELECT *
    FROM myFts
    WHERE myFts MATCH 'term'
    AND languageId = 1
UNION ALL
    SELECT m.*
    FROM myFts m LEFT OUTER JOIN term_search1 t ON (m.id = t.id)
    WHERE myFts MATCH 'term'
    AND m.languageId = 2
    AND t.id IS NULL
)
SELECT * FROM term_search1

これもうまくいきませんでした。どうやら、彼は languageId = 2 に対して 2 つのルックアップを実行したようです (これはおそらくバグですか?)。

前もって感謝します :)

4

2 に答える 2

4

TEMPORARY テーブルを使用して、myFts へのクエリの数を 2 に減らすことができます。

CREATE TEMP TABLE results (id INTEGER PRIMARY KEY);

INSERT INTO results 
    SELECT id FROM myFts
    WHERE myFts MATCH 'term' AND languageId = 1;

INSERT INTO results
    SELECT id FROM myFts
    WHERE myFts MATCH 'term' AND languageId = 2
    AND id NOT IN (SELECT id FROM results);

SELECT * FROM myFts
    WHERE id IN (SELECT id FROM results);

DROP TABLE results;

スキーマを変更できる場合は、FTS テーブルにテキスト データのみを保持する必要があります。こうすることで、数値を検索していて行の一致languageIdが望ましくない場合に、誤った結果を回避できます。非テキスト データ (idや など) を保持する別のメタ テーブルを作成し、のlanguageIdに対して結合して行をフィルター処理します。この方法では、FTS テーブルを 1 回だけクエリする必要があります。一時テーブルを使用して FTS テーブルの結果を保存し、メタ テーブルを使用してそれらを並べ替えます。rowidmyFts

于 2015-07-24T11:45:59.343 に答える
2

これは私が考えることができる最高のものです:

SELECT *
FROM myFts t1
JOIN (SELECT COUNT(*) AS cnt, id 
      FROM myFts t2
      WHERE t2.languageId in (1, 2) 
      AND t2.myFts MATCH 'term'
      GROUP BY t2.id) t3
ON t1.id = t3.id
WHERE t1.myFts MATCH 'term'
    AND t1.languageId in (1, 2) 
    AND (t1.languageId = 1 or t3.cnt = 1)

2番目のMATCH句が必要かどうかはわかりません。アイデアは、最初に許容可能な行を数えてから、最適な行を選択することです。

編集:なぜあなたのテーブルでうまくいかないのか分かりません。これは私がそれをテストするために行ったことです(SQLiteバージョン3.8.10.2):

CREATE VIRTUAL TABLE myFts USING fts4(
  id integer,
  languageId integer,
  content TEXT
);

insert into myFts(id, languageId, content) values (10, 1, 'term 10 lang 1');
insert into myFts(id, languageId, content) values (10, 2, 'term 10 lang 2');
insert into myFts(id, languageId, content) values (11, 1, 'term 11 lang 1');
insert into myFts(id, languageId, content) values (12, 2, 'term 12 lang 2');
insert into myFts(id, languageId, content) values (13, 1, 'not_erm 13 lang 1');
insert into myFts(id, languageId, content) values (13, 2, 'term 13 lang 2');

クエリを実行すると、次のようになります。

sqlite> SELECT *
   ...> FROM myFts t1
   ...> JOIN (SELECT COUNT(*) AS cnt, id 
   ...>       FROM myFts t2
   ...>       WHERE t2.languageId in (1, 2) 
   ...>       AND t2.myFts MATCH 'term'
   ...>       GROUP BY t2.id) t3
   ...> ON t1.id = t3.id
   ...> WHERE t1.myFts MATCH 'term'
   ...>     AND t1.languageId in (1, 2) 
   ...>     AND (t1.languageId = 1 or t3.cnt = 1);
10|1|term 10 lang 1|2|10
11|1|term 11 lang 1|1|11
12|2|term 12 lang 2|1|12
13|2|term 13 lang 2|1|13
sqlite> 
于 2015-07-27T17:16:39.460 に答える