1

次のテーブルがあります。

  • books : 本のリスト
  • tags : タグのリスト (小説、伝記など)
  • xrefbookstags : 相互参照テーブル (本には複数のタグがあります)

がやりたいことは、タグのセットが与えられ、タグがセットと完全に一致する本を見つけることです。これが私が試したことです:

SELECT B.name, B.author, B.id, B.finished, B.manual
    FROM books B INNER JOIN xrefbookstags XRBT 
        ON XRBT.idBooks=B.id JOIN tags T
        ON XRBT.idTags=T.id
    WHERE T.name IN ('novel','biography')

これにより、セットの同等性ではなく包含が得られます。そのため、セット内のタグの数を数えて、セットのサイズと一致させる必要があると考えました。

SELECT B.name, B.author, B.id, B.finished, B.manual
    FROM books B INNER JOIN xrefbookstags XRBT 
        ON XRBT.idBooks=B.id JOIN tags T
        ON XRBT.idTags=T.id
    WHERE T.name IN ('novel','biography') AND count(T.id)=2

しかし、これは失敗します。ここに私の質問があります: この相互参照コンテキストでセットを一致させることはできますか?

4

3 に答える 3

2

あなたはそれらの本が欲しいです

a) その本に必要なタグが欠落していない、かつ b) その本に必要なタグのリストにないタグがない。

これらのタグ名の ID をテーブルに入れ (ここではオンザフライで実行)、これを実行します。(SQL Fiddle や CREATE TABLE/INSERT のサンプル データがないため、すべての詳細を確認するためにこれを実行しませんでした)

WITH TagIDs(id) AS ( --required tag ids
  SELECT id
  FROM tags
  WHERE name in ('novel', 'biography')
)

  SELECT B.name, B.author, B.id, B.finished, B.manual --select those books
  FROM books B

  WHERE --a)
  NOT EXISTS (  -- there is no
    SELECT * FROM TagIDs --  required tag
    WHERE TagIDs.id NOT IN ( -- that's missing
      SELECT XRBT.idTags FROM xrefbookstags XRBT -- from the list of tags
      WHERE XRBT.idBooks=B.id -- for this particular book
    )
  )
  AND --b)
  NOT EXISTS ( -- there is no
    SELECT * FROM xrefbookstags XRBT --tag
    WHERE XRBT.idBooks=B.id --for this particular book
    AND XRBT.idTags NOT IN ( --that's missing
      SELECT id FROM TagIDs --from the list of required tags
    )
  )
于 2013-08-11T22:42:16.833 に答える
1

EXISTSとを組み合わせることができますNOT EXISTS

SELECT B.name, B.author, B.id, B.finished, B.manual
FROM books B
WHERE EXISTS(
    SELECT 1 FROM xrefbookstags XRBT 
    INNER JOIN Tags T ON XRBT.idTags=T.id
    WHERE XRBT.idBooks=B.id
    AND T.name = 'novel'
)
AND EXISTS(
    SELECT 1 FROM xrefbookstags XRBT 
    INNER JOIN Tags T ON XRBT.idTags=T.id
    WHERE XRBT.idBooks=B.id
    AND T.name = 'biography'
) 
AND NOT EXISTS(
    SELECT 1 FROM xrefbookstags XRBT 
    INNER JOIN Tags T ON XRBT.idTags=T.id
    WHERE XRBT.idBooks=B.id
    AND T.name NOT IN ('novel','biography')
)
于 2013-08-11T21:20:04.827 に答える
0

これは、「set-within-sets」クエリの例です。最も一般的なアプローチはgroup by、ロジックを使用してhaving句に入れることです。あなたの場合:

SELECT B.name, B.author, B.id, B.finished, B.manual
FROM books B INNER JOIN
     xrefbookstags XRBT 
     ON XRBT.idBooks = B.id JOIN
     tags T
     ON XRBT.idTags = T.id
group by B.name, B.author, B.id, B.finished, B.manual
having sum(case when t.name = 'novel' then 1 else 0 end) > 0 and
       sum(case when t.name = 'biography' then 1 else 0 end) > 0 and
       sum(case when t.name not in ('novel', 'biography') then 1 else 0 end) = 0;

ロジックは次のとおりです。最初の句は、少なくとも 1 つのタグが である場合に true です'novel'。少なくとも 1 つの句が存在する場合、2 番目の句は true に'biography'なり、他のタグがない場合、3 番目の句は true になります。

これは簡単に一般化できます。これらの 2 つのタグがあり、他のタグを持つ可能性のある書籍が必要な場合は、3 番目の句を省略します。

having sum(case when t.name = 'novel' then 1 else 0 end) > 0 and
       sum(case when t.name = 'biography' then 1 else 0 end) > 0;

どちらか一方の本が必要な場合:

having sum(case when t.name = 'novel' then 1 else 0 end) > 0 or
       sum(case when t.name = 'biography' then 1 else 0 end) > 0;

これらの 2 つに加えて「歴史的」な本が必要な場合は、次のように追加します。

having sum(case when t.name = 'novel' then 1 else 0 end) > 0 and
       sum(case when t.name = 'biography' then 1 else 0 end) > 0 and
       sum(case when t.name = 'historical' then 1 else 0 end) > 0;

そして、あなたがそれを望んでいたが、料理についてではなかった場合:

having sum(case when t.name = 'novel' then 1 else 0 end) > 0 and
       sum(case when t.name = 'biography' then 1 else 0 end) > 0 and
       sum(case when t.name = 'historical' then 1 else 0 end) > 0 and
       sum(case when t.name = 'cooking' then 1 else 0 end) = 0;

編集:

一致させたいタグのコンマ区切りリストがある場合は、次のことができます。

having sum(case when ','+@List+',' not like '%,'+t.name+',%' then 1 else 0 end) = 0 and
       count(distinct t.name) = 1 + len(@list) - len(replace(@list, ',', ''))

最初の節は、すべてのタグがリストにあることを示しています。2 番目は、タグの長さがリストの長さであることを示しています。

これは基本的に疑似コードです。データベースが異なれば、len()関数の名前も異なり、文字列を連結する方法も異なり、変数値を表現する方法も異なります。しかし、意図は明確でなければなりません。

于 2013-08-11T22:20:57.507 に答える