2017-06-15 4 views
2

私はこのSQLパズルを解決しようとしています。私はほとんどそれを理解しましたが、最後の部分はちょっと難しいです。 結合を結合して結合テーブルに存在

は私が持っているキャンペーンのテーブルとユーザーテーブル

キャンペーンのステータスを持っている:非アクティブ/アクティブ。

さらに、キャンペーンとユーザーの間にjoinテーブルがあります:campaign_users。この表は、一部のユーザーにキャンペーンを排他的に割り当てるために使用されます。私はいくつかの「面」を組み合わせたクエリを作りたい

Campaigns 
id name  status 
1 campaign_a active 
2 campaign_b active 
3 campaign_c active 
4 campaign_d inactive 

Users 
id name 
1 user_a 
2 user_b 
3 user_c 

Campaign_users 
id campaign_id  user_id 
1 2    1 
2 3    2 
3 4    1 

は、このデータを考えます。

ステップ1:私は最後の部分にこだわって言ったように、ここで私のステップで選択してアクティブなキャンペーン:

SELECT campaigns.* 
FROM campaigns 
WHERE campaigns.status = 'active' 

output 
campaign: 1,2,3 

ステップ2:(と)排他的に誰にも割り当てられていません。

​​

ステップ3:(と)は、排他的にpassed_in USER_ID(1)に割り当てられています。

SELECT campaigns.* 
FROM campaigns 
WHERE campaigns.status = 'active' 
INNER JOIN campaign_users ON campaign_users.campaign_id = campaigns.id 
AND campaign_users.user_id = 1 

output: 
campaign: 2 

最終ステップ:

は、これは私がこだわっている部分です。

all 'facets'をまとめて組み合わせるクエリ:排他的ではなく(誰にも割り当てられていない)、渡されたuser_id専用のアクティブなキャンペーンのみ。

これは少し矛盾しているように感じますが、内部結合は結合表に存在するキャンペーンのみを返しますが、 '非排他的'キャンペーンはスキップします。

expected output: 

with passed_in user_id of 1 

campaign: 1,2 (Active & non exclusive and exclusively assigned to user 2) 

with passed_in user_id of 2 

campaign: 1,3 (Active & non exclusive and exclusively assigned to user 2) 

with passed_in user_id of 3 

campaign: 3 (Active & non exclusive but no exclusive assigned to user 3) 
+0

あなたの問題は、必要がないときに参加することに起因します。あなたがする必要がないときに参加しないでください。条件は 'FROM' /' JOIN'節にない 'WHERE'節に属します。ユーザーのための排他的なキャンペーンを取得するには、通常、 'EXISTS'または' IN'節を使用します(疑似コード: "どこにユーザーのエントリが存在するか")。最終的なクエリは、2つの条件を「OR」:「存在しない(...)または存在する(...)」と組み合わせることになる。しかし、あなたの場合、より簡単なテクニックを使うことができます。私の答えを見てください。 –

答えて

1

あなたは、単純なトリックを使用することができます:外側はcampaignsにすべてcampaign_usersに参加します。排他的キャンペーンのキャンペーンとユーザーごとに行が表示され、非排他的キャンペーンの場合はnullが表示されます。問題のユーザー(ユーザー1)またはすべてのユーザー(ユーザーnull)のすべてのキャンペーンを保持します。

select c.* 
from campaigns c 
left join campaign_users cu on cu.campaign_id = c.id 
where c. status = 'active' and (cu.user_id = 1 or cu.user_id is null); 
+0

これはまさに私が欲しいものです!そのトリックは別のタイプの結合を使用していましたか?なぜこれが機能するのか詳しく説明できますか?それは単に結合のためか、 'cu.user_id = 1またはcu.user_id is null'ですか? –

+1

ええ、私は私の答えでこれを説明したと思った...あなたがまだ外側の結合を知らない場合は、それらを見てください。彼らは、内部の結合に加えて、第2のテーブルに一致しない第1のテーブルの行をすべてあなたに与えます。そこで、ユーザーnullが「すべてのユーザー」を意味するキャンペーン・ユーザーの関連付けのリストを生成します。 where節では、ファー・ユーザー1と「すべてのユーザー」のレコードを選択します(したがって、他のユーザーの排他的キャンペーン・ユーザーの関連付けのみを排除します)。途中の結果と、私たちが保持するものと、却下するものについては、where節なしで試してみてください。 –

0

あなたは例えば、必要な結果を得るためにステップ2のためのクエリと3をUNIONことができます。:

SELECT campaign_id 
FROM campaigns 
WHERE campaigns.status = 'active' 
WHERE NOT EXISTS(select * FROM campaign_users WHERE campaign_users.campaign_id = campaigns.id) 

UNION 

SELECT campaign_id 
FROM campaigns 
WHERE campaigns.status = 'active' 
INNER JOIN campaign_users ON campaign_users.campaign_id = campaigns.id 
AND campaign_users.user_id = ? 
関連する問題