2

私はテーブルを持っています:

テーブルサイト

╔════╦═══════════════╗
║ ID ║     NAME      ║
╠════╬═══════════════╣
║  1 ║ stackoverflow ║
║  2 ║ google.com    ║
║  3 ║ yahoo.com     ║
║  4 ║ cnn.com       ║
╚════╩═══════════════╝

テーブルウィジェット

╔════╦════════════╗
║ ID ║    NAME    ║
╠════╬════════════╣
║  1 ║ polling    ║
║  2 ║ comments   ║
║  3 ║ newsletter ║
║  4 ║ mail       ║
╚════╩════════════╝

テーブル SiteWidget

╔═════════╦═══════════╗
║ SITE_ID ║ WIDGET_ID ║
╠═════════╬═══════════╣
║       1 ║         1 ║
║       1 ║         2 ║
║       2 ║         2 ║
║       2 ║         3 ║
║       4 ║         2 ║
║       3 ║         1 ║
║       3 ║         3 ║
║       1 ║         4 ║
║       3 ║         4 ║
║       4 ║         1 ║
║       4 ║         4 ║
╚═════════╩═══════════╝

コメント (2) とメール (4) があるすべてのサイトを取得したいと思います。

私は試します:

SELECT * FROM Site 
LEFT JOIN SiteWidget ON Site.id = SiteWidget.site_id 
WHERE SiteWidget.widget_id IN (2, 4) 

しかし、これは私にstackoverflow(2、4 - OK)、google.com(2 - NOT OK - 4なし)、yahoo.com(4 - NOT OK、2なし)、およびcnn.com(2、4 - OK)を返します。2 と 4 ですべてのサイトを取得するにはどうすればよいですか? 一人じゃなくていつも一緒。

4

7 に答える 7

4

これを行う 1 つの方法を次に示します。追加の結合を使用して、2 つのウィジェットの組み合わせを探すことができます。

SELECT * FROM Site s
INNER JOIN SiteWidget w1 ON (s.id = w1.site_id)
INNER JOIN SiteWidget w2 ON (s.id = w2.site_id)
WHERE w1.widget_id=2 and w2.widget_id=4;
于 2013-02-03T10:30:15.083 に答える
3

この問題は と呼ばれRelational Divisionます。

SELECT  a.Name
FROM    Site a
        INNER JOIN SiteWidget b
            ON a.ID = b.Site_ID
        INNER JOIN Widget c
            ON b.Widget_ID = c.ID
WHERE   c.Name IN ('comments','mail')
GROUP   BY a.Name
HAVING  COUNT(*) = 2

widget_idすべてに対して一意性が強制されていない場合はsite_idDISTINCTキーワードが必要です。

SELECT  a.Name
FROM    Site a
        INNER JOIN SiteWidget b
            ON a.ID = b.Site_ID
        INNER JOIN Widget c
            ON b.Widget_ID = c.ID
WHERE   c.Name IN ('comments','mail')
GROUP   BY a.Name
HAVING  COUNT(DISTINCT c.Name) = 2

その他のリンク

于 2013-02-03T10:23:54.777 に答える
2

試す:

SELECT * FROM Site
INNER JOIN SiteWidget SW1
    ON SW1.widget_id = 2 
    AND Site.id = SW1.site_id
INNER JOIN SiteWidget SW2
    ON SW2.widget_id = 4
    AND Site.id = SW2.site_id
于 2013-02-03T10:20:53.723 に答える
1

これが別の方法です、フィドル(フィドルテーブルとデータをありがとう@JW)

select s.id, s.name
from site s join (
   select sw.site_id, count(w.id) cnt
   from SiteWidget sw join widget w on sw.widget_id = w.id 
   where w.id in (2,4) 
   group by sw.site_id
) T on s.id = T.site_id and T.cnt = 2
于 2013-02-03T10:36:49.277 に答える
1

文字通り、2 つの異なる JOIN が必要になります。

SELECT * FROM Site
    JOIN SiteWidget AS mail     ON (Site.id = mail.site_id AND mail.widget_id = 4)
    JOIN SiteWidget AS comments ON (Site.id = comments.site_id AND comments.widget_id = 2);

たとえば、(site_id, widget_id) が MtM リレーションに対して通常行われるように主キーであるなどの理由で、SiteWidget テーブルに重複がないことが確実な場合は、HAVING を使用することもできます: これは MySQL 構文です:

SELECT Site.* FROM Site
    JOIN SiteWidget ON (SiteWidget.site_id = Site.id AND widget_id IN (2,4))
    GROUP BY Site.id HAVING COUNT(*) = 2;

一意性のため、サイトが 2 回表示される唯一の可能性は、両方のウィジェットを持つことです。これは の乱用でGROUP BYあると考える人もいれば、一部の SQL (私の記憶が正しければ PostgreSQL) では、 group-by 列に機能的に依存している場合でも、SiteのフィールドGROUP BYまたは集計関数を表示する必要があります。SELECTSite.id

最初の式の方が明確で安全であることがわかり、2 番目の式とほぼ同じくらい速いと思います。

これは、多対多の結合テーブルが非常に小さい (そして起動時にインデックスがカバーされている) ことと、この種の操作が初日から標準的であり、最も最適化されているものの 1 つであるためです。たとえば、widget_id 2 と 4 のチェックが、結合バッファー内の SiteWidget テーブルの単一の論理読み取りと並行して実行されることを期待しています。そうでない場合でも、単一の物理読み取りと並行してロードされ、もう 1 つは SQL キャッシュ、または少なくとも IOSS キャッシュにヒットする可能性があります。

このわずかなバリエーションを試すこともできます。これはより高速です。

SELECT Site.* FROM Site
    JOIN SiteWidget AS mail     ON (Site.id = mail.site_id AND mail.widget_id = 4)
    JOIN SiteWidget AS comments ON (mail.site_id = comments.site_id AND comments.widget_id = 2);

これは、最小の SiteWidget テーブルに対してメインの JOIN を実行し、Site への ID ルックアップを実行する必要があります。これは、最初のインスタンスのようにクエリを記述した場合でも、実際に行われる可能性が高いことです。

pollingたとえば、ウィジェットを追加する必要がある場合は、最初の式をコピーして貼り付けることで簡単に拡張できます。

于 2013-02-03T10:50:46.520 に答える
1

ウィジェット名でフィルタリングする場合は、これを使用できます

SELECT
    S.id,
    S.name 
FROM Site S
    JOIN SiteWidget SW
        ON S.id = SW.site_id
    JOIN Widget W
        ON SW.widget_id = W.id
WHERE W.name IN ('comments', 'mail')
GROUP BY S.Id,S.name
HAVING COUNT(DISTINCT W.name) = 2

またはウィジェットIDでフィルタリングしたい場合

SELECT
    S.id,
    S.name 
FROM Site S
    JOIN SiteWidget SW
        ON S.id = SW.site_id
WHERE SW.widget_id IN (2, 4)
GROUP BY S.Id,S.name
HAVING COUNT(DISTINCT SW.widget_id) = 2
于 2013-02-03T10:24:12.123 に答える
1

2回参加する必要があります

SELECT * FROM Site 
inner JOIN SiteWidget m ON Site.id = m.site_id and m.widget_id = 4
inner Join SiteWidget c ON Site.id = c.site_id and c.widget_id = 2
于 2013-02-03T10:27:40.220 に答える