3

以下の列を含む3つのテーブルがあります。

トピック:
[トピックID] [トピック名]
メッセージ:
[メッセージID] [メッセージテキスト]
MessageTopicRelations
[エントリID] [メッセージID] [トピックID]

メッセージは複数のトピックに関するものです。質問: いくつかのトピックが与えられた場合、これらすべてのトピックに関するメッセージを取得する必要がありますが、他のトピックに関するメッセージも含まれる可能性があります。これらの特定のトピックの一部に関するメッセージは含まれません。私の要求をうまく説明できたと思います。それ以外の場合は、サンプル データを提供できます。ありがとう

4

4 に答える 4

5

次の例では、 、 、および をトピック ID の代わりに使用xyzいます。例として何も提供されていません。

JOIN の使用:

SELECT m.*
  FROM MESSAGES m
  JOIN MESSAGETOPICRELATIONS mtr ON mtr.messageid = m.messageid
  JOIN TOPICS tx ON tx.topicid = mtr.topicid
                AND tx.topicid = x
  JOIN TOPICS ty ON ty.topicid = mtr.topicid
                AND ty.topicid = y
  JOIN TOPICS tz ON tz.topicid = mtr.topicid
                AND tz.topicid = z

GROUP BY/HAVING COUNT(*) の使用:

  SELECT m.*
    FROM MESSAGES m
    JOIN MESSAGETOPICRELATIONS mtr ON mtr.messageid = m.messageid
    JOIN TOPICS t ON t.topicid = mtr.topicid
   WHERE t.topicid IN (x, y, z)
GROUP BY m.messageid, m.messagetext
  HAVING COUNT(*) = 3

2 つのうち、JOIN アプローチの方が安全です。

MESSAGETOPICRELATIONS.TOPICIDGROUP BY/HAVING は、主キーの一部であるか、重複しないように一意のキー制約を持っていることに依存しています。そうしないと、同じトピックの 2 つ以上のインスタンスがメッセージに関連付けられる可能性があります。これは誤検知となります。を使用HAVING COUNT(DISTINCT ...すると誤検知が解消されますが、サポートはデータベースに依存します。MySQL は 5.1+ でサポートしていますが、4.1 ではサポートしていません。Oracle は、SQL Server でテストするために月曜日まで待たなければならないかもしれません...

TOPICSテーブルへの結合が不要であるというビルのコメントを調べました。

SELECT m.*
  FROM MESSAGES m
  JOIN MESSAGETOPICRELATIONS mtr ON mtr.messageid = m.messageid
                                AND mtr.topicid IN (x, y, z)

IN...は偽陽性を返します -句で定義された値の少なくとも 1 つに一致する行。と:

SELECT m.*
  FROM MESSAGES m
  JOIN MESSAGETOPICRELATIONS mtr ON mtr.messageid = m.messageid
                                AND mtr.topicid = x
                                AND mtr.topicid = y
                                AND mtr.topicid = z

...topicid一度にすべての値を取得することはできないため、何も返しません。

于 2010-01-03T03:01:22.037 に答える
1

これは非常に洗練されていない解決策です

SELECT
     m.MessageID
    ,m.MessageText
FROM
    Messages m
WHERE
    m.MessageID IN (
    SELECT
        mt.MessageID
    FROM
        MessageTopicRelations mt
    WHERE
        TopicID IN (1,4,5)// List of topic IDS
    GROUP BY
        mt.MessageID
    HAVING
        count(*) = 3 //Number of topics
    )
于 2010-01-03T02:56:11.007 に答える
1

編集:私のアプローチの欠陥を見つけてくれた@Paul Creaseyと@OMG Poniesに感謝します。
これを行う正しい方法は、トピックごとに自己結合を使用することです。主要な回答に示されているように。


別の非常に洗練されていないエントリ:

select m.MessageText
       , t.TopicName
  from Messages m
       inner join MessageTopicRelations mtr
       on mtr.MessageID = m.MessageID
       inner join Topics t
       on t.TopicID = mtr.TopicID
   and
       t.TopicName = 'topic1'

UNION 

select m.MessageText
       , t.TopicName
  from Messages m
       inner join MessageTopicRelations mtr
       on mtr.MessageID = m.MessageID
       inner join Topics t
       on t.TopicID = mtr.TopicID
   and
       t.TopicName = 'topic2'
...
于 2010-01-03T03:00:12.007 に答える
1

TOPICSRe: OMG Ponies の回答です。テーブルに参加する必要はありません。このHAVING COUNT(DISTINCT)節は MySQL 5.1 で正常に機能します。私はちょうどそれをテストしました。

これが私が意味することです:

GROUP BY/HAVING COUNT(*) の使用:

  SELECT m.*
    FROM MESSAGES m
    JOIN MESSAGETOPICRELATIONS mtr ON mtr.messageid = m.messageid
   WHERE mtr.topicid IN (x, y, z)
GROUP BY m.messageid
  HAVING COUNT(DISTINCT mtr.topicid) = 3

私が提案する理由COUNT(DISTINCT)は、列(messageid,topicid)に一意の制約がない場合、重複が発生する可能性があり、3 つ未満の個別の値であっても、グループ内のカウントが 3 になる可能性があるためです。

于 2010-01-04T06:01:57.177 に答える