【问题标题】:Joining 3 tables and matching null entries连接 3 个表并匹配空条目
【发布时间】:2019-10-10 23:34:57
【问题描述】:

我有 3 个表,用户、新闻、news_viewed。我正在尝试加入这 3 个表格并查找每个用户尚未查看的新闻列表。

  • users
    • 用户名
    • 用户名
    • 状态
  • news
    • 新闻
    • 标题
    • post_time
  • news_viewed
    • nvid
    • 用户名
    • 新闻

希望从users 中查找尚未阅读新闻的列表(在news_viewed 中找到)

我尝试了许多不同的连接,包括左连接和内部和外部,但无法获得我需要的结果。

$_30daysago = strtotime('-30 days');
SELECT * FROM
(
    SELECT users.username, news_id 
    FROM users inner join news_viewed ON
    users.username = news_viewed.username and users.status='active'
    UNION
    SELECT news_viewed.username, post_time 
    FROM news_viewed inner join news ON
    news_viewed.newsid = news.newsid and news.post_time>'$_30daysago'
) as JoinedTable

我需要包含 users.username、news.newsid 和 news.title 的所需结果。

任何帮助将不胜感激,谢谢!

【问题讨论】:

标签: php mysql sql join


【解决方案1】:

这是使用LEFT JOIN 反模式的好地方:

SELECT u.username, n.newsid, n.title
FROM users u
INNER JOIN news n ON n.post_time > ?
LEFT JOIN news_viewed nv 
    ON n.newsid = nv.newsid
    AND nv.username = u.username 
WHERE 
    u.status = 'active' 
    AND nv.nvid IS NULL

此查询生成用户和最近新闻的笛卡尔积(即发布时间大于? 指示的参数),并返回在 news_viewed 上左连接未成功的用户/新闻元组(因此反模式)。

注意:不清楚在联接中使用什么列;列名news_viewed (username) 倾向于表明它与users(username) 相关,而users 的主键似乎是userid。修正列名或修正关系。

【讨论】:

  • 谢谢,但表news 也必须包括在内才能找到可用的新闻。然后查询news_viewed,查看用户没有阅读的内容。
  • @cr1to:我刚刚将news 表添加到查询中。
  • @cr1to:我一开始误读了你的问题。我刚刚更新了我的答案,如果这就是你要找的,请告诉我。
【解决方案2】:

详细说明@GMB 的答案

您的查询:

$_30daysago = strtotime('-30 days');
SELECT * FROM
(
    SELECT users.username, news_id 
    FROM users inner join news_viewed ON
    users.username = news_viewed.username and users.status='active'
    UNION
    SELECT news_viewed.username, post_time 
    FROM news_viewed inner join news ON
    news_viewed.newsid = news.newsid and news.post_time>'$_30daysago'
) as JoinedTable

在说:

让所有活跃用户了解他们已阅读的新闻(内联)

SELECT users.username, news_id 
FROM users inner join news_viewed ON
users.username = news_viewed.username and users.status='active'

并添加过去 30 天内阅读过新闻的用户的所有新闻(再次内联)

SELECT news_viewed.username, post_time 
FROM news_viewed inner join news ON
news_viewed.newsid = news.newsid and news.post_time>'$_30daysago'

这实际上是从 news_viewed 中提取所有元组,减去用户不活跃且新的超过 30 天的元组。

但是,考虑到内部联接的使用,您会带来很多重复记录

1.- 新的少于 30 天的第一个查询的结果

2.- 用户处于活动状态的第二个查询的结果

由于您使用的是 UNION 而不是 UNION ALL,因此您隐含地要求 SELECT DISTINCT,但字段不同(在同一字段中显示 newsid 和 post_time 是没有意义的) 另外,您在字段名称中有错字,不是 news_id


你必须从另一个角度看待它。每个用户都阅读了每个新内容的情况下的潜在组合数量。因此,您以该宇宙为基础(用户数量乘以新闻数量),然后

1- 移除非活跃用户

2- 删除超过 30 天的新闻

3-删除news_viewed表中不相关的元组

SELECT users.username, news.newsid
FROM users
JOIN news 
   ON users.status='active'  -- removes inactive users
   AND news.post_time>'$_30daysago' -- removes older news
LEFT JOIN 
   news_viewed nv USING (username, newsid) 
WHERE nv.nvid IS NULL -- removes unrelated entries

【讨论】:

  • 感谢您详细说明这有助于我更好地理解我做错了什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-28
  • 1970-01-01
  • 1970-01-01
  • 2015-03-24
  • 2012-04-05
  • 2012-02-12
相关资源
最近更新 更多