【问题标题】:Combine join and exists on join table合并连接并存在于连接表上
【发布时间】:2017-11-17 16:56:13
【问题描述】:

我一直在解决这个 sql 难题,我几乎想通了,但最后一部分有点挑战性。

我有一个活动表和一个用户表

活动的状态为:活动/非活动。

除此之外,活动和用户之间还有一个连接表: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 步:(和)不完全分配给任何人:

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

output:
campaign: 1

第 3 步:(和)独占分配给传入的 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

最后一步:

这是我卡住的部分。

查询以将所有“方面”组合在一起:仅非排他性(未分配给任何人)且对传入的 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)

【问题讨论】:

  • 你的问题源于不需要的时候加入。非必要时不要加入。条件属于WHERE 子句而不属于FROM/JOIN 子句。要为用户获取独家广告系列,您通常会使用EXISTSIN 子句(伪代码:“其中存在用户条目”)。然后,最终查询会将这两个条件与OR 结合起来:WHERE NOT EXISTS (...) OR EXISTS (...)。但是,在您的情况下,您可以使用更简单的技术。请参阅我对此的回答。

标签: mysql join not-exists


【解决方案1】:

您可以使用一个简单的技巧:将所有campaign_users 外连接到campaigns。对于独家广告系列,您将获得每个广告系列和用户的一行,对于非独家广告系列,您将获得一个用户为 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);

【讨论】:

  • 这正是我想要的!所以诀窍在于使用另一种类型的连接?你能详细说明为什么这有效吗?是因为加入还是cu.user_id = 1 or cu.user_id is null
  • 嗯,我想我已经在回答中解释了这一点……如果您还不知道外连接,请查找它们。他们给你所有的行,一个内部连接会加上第一个表中在第二个表中没有匹配的行。因此,我们生成了一个活动用户关联列表,其中 user null 表示“所有用户”。在 where 子句中,我们选择用户 1 的记录和“所有用户”的记录(因此只消除了其他用户的独占活动用户关联)。尝试不使用 where 子句以查看中间结果以及我们将保留什么以及我们将忽略什么。
【解决方案2】:

您可以UNION第2步和第3步的查询以获得所需的结果,例如:

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 = ?

【讨论】:

    猜你喜欢
    • 2018-05-23
    • 2022-01-09
    • 2019-03-02
    • 2014-05-14
    • 1970-01-01
    • 2014-05-29
    • 1970-01-01
    • 2023-02-14
    • 2014-06-26
    相关资源
    最近更新 更多