连接多个表,保持NULL

时间:2014-01-22 23:02:28

标签: mysql sql

我有4个表userpostsfollowsnotificationsposts表中有一个名为privacy的字段,其中值1表示“任何人都可以看到”,2表示“只有朋友可以看到”。

我的notifications表格如下:

id user_id tonotify_id notification post_id
1  2       3           ---          1
2  3       2           ---          2
3  2       4           ---          3

我的follows表看起来像这样:

id user_id tofollow_id status fstatus
1  1       2           1      1
2  1       3           1      1

字段fstatus值为1表示他们也是朋友。

现在我正在尝试让某个用户关注的人发送所有通知(比如说id为1的用户)。假设1跟随用户2和3,我需要从上表中获取所有通知。但是,在此之前,我需要确保帖子(由tonotify_id列中的用户发布)可用。也就是说,我需要检查隐私设置。我有以下代码:

SELECT
  u.username AS sender,
  ux.username AS receiver,
  p.id
FROM notifications n
JOIN follows f ON (n.user_id = f.tofollow_id)
JOIN follows fr ON (n.tonotify_id = fr.tofollow_id)
JOIN user u ON (u.id = n.user_id)
JOIN user ux ON (ux.id = n.tonotify_id)
LEFT JOIN posts p ON (n.posts_id = p.id)
WHERE f.user_id = 1
  AND fr.user_id = 1
  AND f.status = 1
  AND p.privacy = 1 OR (p.privacy = 2 AND fr.fstatus = 1)
ORDER BY n.id DESC

除了一个小故障外,它似乎正在起作用。由于用户1没有关注用户4所有针对4的通知,即使帖子是公开的也不显示。我的猜测与JOIN follows fr ON (n.tonotify_id = fr.tofollow_id)有关,因为用户1没有跟随4,因此没有与此连接匹配的行。有什么建议可以解决这个问题吗?

2 个答案:

答案 0 :(得分:1)

  

我确实尝试了[外连接],但输出是相同的。

当您使用外部联接,然后在WHERE子句中的相等性检查中使用其中一个“外部”列时,将外部联接转换为内部联接。这是因为您检查帖子隐私的条件需要帖子在那里:

AND p.privacy = 1 OR (p.privacy = 2 AND fr.fstatus = 1)

当外部联接即将生成与没有帖子的通知相对应的行时,它将检查上述条件。由于帖子不存在,p.privacy将评估为NULL,“污染”OR的两侧,并最终使整个条件评估为false

将此条件移至联接的ON条件将解决问题:

SELECT
  u.username AS sender,
  ux.username AS receiver,
  p.id
FROM notifications n
JOIN follows f ON (n.user_id = f.tofollow_id)
JOIN follows fr ON (n.tonotify_id = fr.tofollow_id)
JOIN user u ON (u.id = n.user_id)
JOIN user ux ON (ux.id = n.tonotify_id)
LEFT JOIN posts p ON (n.posts_id = p.id)
                 AND (p.privacy = 1 OR (p.privacy = 2 AND fr.fstatus = 1))
WHERE f.user_id = 1
  AND fr.user_id = 1
  AND f.status = 1
ORDER BY n.id DESC

解决此问题的另一种方法是向IS NULL添加OR条件,如下所示:

SELECT
  u.username AS sender,
  ux.username AS receiver,
  p.id
FROM notifications n
JOIN follows f ON (n.user_id = f.tofollow_id)
JOIN follows fr ON (n.tonotify_id = fr.tofollow_id)
JOIN user u ON (u.id = n.user_id)
JOIN user ux ON (ux.id = n.tonotify_id)
LEFT JOIN posts p ON (n.posts_id = p.id)
WHERE f.user_id = 1
  AND fr.user_id = 1
  AND f.status = 1
  AND (p.privacy IS NULL OR p.privacy = 1 OR (p.privacy = 2 AND fr.fstatus = 1))
ORDER BY n.id DESC

答案 1 :(得分:0)

您需要使用LEFT JOIN之类的外部联接,而不是仅使用JOIN使用内部联接。外连接将始终从一个“侧”返回行(这就是为什么它被称为LEFT [OUTER] JOINRIGHT [OUTER] JOIN),即使另一个“侧”与任何东西都不匹配。