1

私はオンラインライブラリの検索エンジンに取り組んでいますが、ここで立ち往生しています。タグを検索する場合、OR検索(つまり、「tag1」または「tag2」の本)は正常に機能しますが、AND検索は問題を引き起こします。

これに使用するテーブル(およびその列)は次のとおりです。

books  | book_id, other_info
tagmap | map_id, book_id, tag_id
tags   | tag_id, tag_text

他の多くの検索オプションをユーザーが有効/無効にできるため、クエリはPHPによって生成されます。「tag1」と「tag2」のタグが付いた本を検索すると、次のクエリが生成されます。

SELECT DISTINCT b.book_id, b.other_info
FROM books b, tagmap tm, tags t
WHERE b.book_id = "NA"
OR ( (t.tag_text IN ("tag1", "tag2"))
      AND tm.tag_id = t.tag_id
      AND b.book_id = tm.book_id )
HAVING COUNT(tm.book_id)=2

WHERE行(結果は表示されません)があり、追加のパラメーターをクエリに簡単に追加できます。これはもっとうまく処理できることを私は知っていますが、今のところそれは問題ではありません。

OR検索(同じクエリですが、HAVING COUNT行がない)を実行すると、データベース内でこれらのタグのいずれかを持つ2冊の本が返されますが、データベース内で両方のタグを持つ1冊の本を検索すると、何も返されません。

クエリの何が問題になっていますか?これはそれを行う方法ではありませんか?私は何を見落としていますか?

ありがとう!

編集:リクエストに応じて、返却する必要のある本に関連する各テーブルのデータ:

books table:
book_id     110

tagmap table:
book_id     110    110
tag_id      15     16

tags table:
tag_id      15     16
tag_text    tag1   tag2

解決策:私がしなければならなかったのは、含めることだけでした

GROUP BY b.book_id

HAVINGCOUNT行の前。そのような単純な。特に検索クエリの最適化を目的としている場合は、tazが提供する回答も検討する価値があります。

4

3 に答える 3

2

FROM句のテーブルのカンマ区切りリストは内部結合のように機能するため、クエリは同じタグIDを持つtagmapsテーブルとtagsテーブルのすべての行を選択し、それらの行のうち、同じブックIDを持つbooksテーブルとtagmapsテーブル。次に、HAVING句では、同じブックIDを持つ結果セットから2つの行を返す必要があります。特定のブックIDを持つブックテーブルには1つの行しか存在できないため(ブックIDがブックテーブルの主キーであると想定)、この条件が満たされることはありません。

必要なのは、ブックテーブルなしの結合です。OR句の結果に2回表示される同じ本のIDを探しているので(私は信じています)、それらの結果で本のテーブルを結合したくないのは、同じ本のIDを結果は複数回あります。

編集:概念的には、基本的に2つの異なるものを組み合わせています。同じ本のタグとタグマップを探しており、それらの各本から本の情報も取得しています。したがって、実際には、tagmapsテーブル内の同じ本IDのすべてのインスタンスに対して重複するother_infoデータを取得し、distinct句を使用して、その重複データを1行に減らします。必要なのは本IDとother_infoだけだからです。これを行うには、2つのクエリまたはサブクエリを使用することを検討します。他の[より良い]方法もあるかもしれません。私はそれを理解するためにそれをいじくり回さなければならないでしょう。

手始めに、試してみてください

SELECT DISTINCT tm.book_id, b.other_info
FROM tagmap tm inner join tags t
    on tm.tag_id = t.tag_id
  left join books b
    on tm.book_id = b.book_id
HAVING count(tm.book_id) = 2
于 2012-05-15T18:00:42.647 に答える
1
SELECT book_id FROM tagmap JOIN tags ON (tag_id) WHERE tag_text = "tag1"
INTERSECT
SELECT book_id FROM tagmap JOIN tags ON (tag_id) WHERE tag_text = "tag2"

このすべてをサブクエリとしてラップして、必要な他の本の情報を選択します。

SELECT book_id, other_info FROM books WHERE book_id IN
(
   ...
)
于 2012-05-15T18:07:02.350 に答える
0

わかりました。mysqlデータベースに対して複雑なソリューションを実行したように見えます(他の場合は問題ないはずです)。したがって、データベース構造は次のとおりです。

source (id)
tag(id)
tags(source, tag)

この要件をカバーします。

  • 選択したタグのいずれか、または選択したすべてのタグを使用してソースをフェッチします
  • 除外されたタグを含まないソースをフェッチする

ここでクエリ:

 SELECT source.id 
 FROM source
 -- optional join, if you need to include sources with tags:
 LEFT JOIN tags tags_include ON source.id = tags_include.source
 -- optional join, if you need to exclude sources with tags
 LEFT JOIN tags tags_exclude ON source.id = tags_exclude.source
      -- here list of excluded tags
      and tags_exclude.tag in(1)
 WHERE
    -- optional condition, tags which you need to include
    tags_include.tag IN (2, 3)
    -- optional condition, which will exlcude sources with excluded tags
    and tags_exclude.source is null
 GROUP BY source.id
 -- optional having, in case when you include tags with "AND" strategy
 -- count should be equal to count of selected tags
 -- if you fetch sources with "OR" strategy, ignore this having
 HAVING count(1) >= 2
 ORDER BY source.id DESC
 LIMIT 0, 50;

したがって、このクエリは、IDが2と3のタグがあり、1のタグがない50個の最新のソースをフェッチします。

于 2018-12-30T13:47:39.170 に答える