【问题标题】:Join 2 tables where one table may or may not have an entry连接 2 个表,其中一个表可能有也可能没有条目
【发布时间】:2021-11-17 18:22:31
【问题描述】:

我有 2 张桌子(人 + 活动)

id name
1 John
2 Axel
3 William

活动

activity_id person_id activity_type
1 1 Login
2 1 Visited Website
3 1 Logout
4 3 Login
5 3 Logout

如您所见,John 和 William 都有多项活动。但是 Axel 根本没有任何活动。

我尝试达到的结果如下。 我想从 person 表的每个条目中选择 id 和 name,并从 activity 表中选择 activity_id 和 activity_type。

如果此人还没有活动,则仍应显示此人的 ID 和姓名。 如果此人的活动不止一项,则只应显示具有最高 id 的活动。

我追求的结果:

id name activity_id activity_type
1 John 3 Logout
2 Axel null null
3 William 5 Logout

当我尝试左连接时:

select p.id, p.name, a.activity_id, a.activity_type
from person p left join activity a on p.id = a.person_id
order by p.id

我得到了这个结果:

id name activity_id activity_type
1 John 1 Login
1 John 2 Visited Website
1 John 3 Logout
2 Axel null null
3 William 4 Login
3 William 5 Logout

但由于我只希望每人一个条目,我添加了以下 where 子句:

select p.id, p.name, a.activity_id, a.activity_type
from person p left join activity a on p.id = a.person_id
where a.id = (select max(id) from activity a2 where a2.person_id = p.id)
order by p.id

这是结果:

id name activity_id activity_type
1 John 3 Logout
3 William 5 Logout

John 和 William 的条目如我所愿。仅显示“最后一个”活动。 问题是 Axel 不再显示。

任何帮助appriciated。 非常感谢!

【问题讨论】:

  • 将 WHERE 切换为 AND。
  • 通过不考虑 NULL 值,您的 WHERE 将 LEFT JOIN 真正转换为 INNER。
  • @jarlh 你做到了 :) 非常感谢!!!

标签: sql join postgresql-9.4


【解决方案1】:

在 Postgres 中,最简单和最快的方法通常是使用 Postgres 对 SQL 的扩展,distinct on

select distinct on (p.id) p.id, p.name, a.activity_id, a.activity_type
from person p left join
     activity a
     on p.id = a.person_id
order by p.id, a.activity_id desc;

distinct on 为括号中指定的键返回一行。具体行由order by确定。

为了性能,我建议这样编写查询:

select p.id, p.name, a.activity_id, a.activity_type
from person p left join
     (select distinct on (a.person_id) a.*
      from activity a
      order by a.person_id, a.activity_id desc
     ) a
     on p.id = a.person_id;

然后在activity(person_id, activity_id desc) 上创建索引。

【讨论】:

  • 成功了。谢谢!但执行时间从 700 毫秒变为 7 秒。这是一个简化的 SQL。我的真实陈述要复杂得多。
【解决方案2】:

您可以使用另一个选项,使用正确的索引可能会更高效,以使用窗口函数来识别每个人的活动

select p.id, p.name, a.activity_id, a.activity_type
from person p 
left join (
    select *, Row_Number() over(partition by person_id order by activity_id desc) rn
    from activities
)a on a.person_id=p.id and a.rn=1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-12
    • 1970-01-01
    相关资源
    最近更新 更多