SQL:如何通过联接进行订购和限制

SQL:如何通过联接进行订购和限制,sql,postgresql,Sql,Postgresql,例如,假设我有以下查询: select u.id, u.name, (select s.status from user_status s where s.user_id = u.id order by s.created_at desc limit 1) as status from user u where u.active = true; 上面的查询非常有效。它返回所选用户的最新用户状态。但是,我想知道如何使用user\u status表上的联接而不是使用子查询来获得

例如,假设我有以下查询:

select
  u.id,
  u.name,
  (select s.status from user_status s where s.user_id = u.id order by s.created_at desc limit 1) as status
from
  user u
where
  u.active = true;
上面的查询非常有效。它返回所选用户的最新用户状态。但是,我想知道如何使用
user\u status
表上的联接而不是使用子查询来获得相同的结果。这样的事情可能吗

我正在使用PostgreSQL


谢谢你能给予的任何帮助

您可以通过将子查询转换为with子句并在联接中使用它来实现它

 select u.id , b.status 
 from user u join user_status b on u.id= b.user_id
 where u.active = true;
 order by b.s.created_at desc limit 1
我认为这是可行的。
但是在你的代码中有一个“b”,我不知道它是什么。

JOIN
语法不直接提供顺序或限制选项;所以严格地说,作为一个连接,你不能直接实现你想要的。我认为解决问题的最简单方法是使用联接子查询,如下所示:

select
       u.id
     , u.name
     , s.status
from user u
left join (
          select
               user_id
             , status
             , row_number() over(partition by user_id
                                 order by created_at desc) as rn
          from user_status s
          ) s on u.id = s.userid and s.rn = 1
where u.active = true;
这里,分析函数
row_number()
over()
子句相结合,使得后续的连接条件
和s.rn=1
能够通过计算rn值来利用连接行的排序和限制

nbselect子句中的相关子查询(在问题的查询中使用)的行为类似于左连接,因为它可以返回NULL。如果不需要或不需要这种效果,可以更改为内部联接

可以将该子查询移动到CTE中,但除非有令人信服的理由,否则我更喜欢使用上述更传统的形式

另一种方法(对于Postgres 9.3或更高版本)是使用与原始子查询非常相似的
lateral
联接,但当它成为from子句的一部分时,可能比在select子句中使用该子查询更有效

select
       u.id
     , u.name
     , s.status
from user u
left join lateral (
       select user_status.status 
       from user_status
       where user_status.user_id = u.id 
       order by user_status.created_at desc 
       limit 1
       ) s ON true
where u.active = true;

我最终做了以下几件事,这对我来说非常有用:

select
  u.id,
  u.name,
  us.status
from
  user u
  left join (
    select
      distinct on (user_id)
      *
    from
      user_status
    order by
      user_id,
      created_at desc
  ) as us on u.id = vs.user_id
where
  u.active = true;

这也比我在问题中的查询更有效。

哇!那
b
应该是
s
。相应地更新了我的问题。另外,您上面提供的查询与我上面写的不完全相同。我相信你们每次只会返回一排。但我上面的查询返回多行。关于如何调整它以使其返回正确的行数,有什么想法吗?正如我看到的,这两个查询返回相同的结果。很抱歉,我帮不了你。你知道你的解决方案的效率会比我在问题中给出的子查询解决方案高还是低吗?在子查询中,你正在点击每个id的用户_状态,而如果你将它具体化,你基本上会重用它。Rest您的解释计划会告诉您。这应该是一个注释。@Ted在不知道我当时无法进行注释的情况下,您将其标记下来。这并没有改变您应该等到能够使用
distinct on()
而不是使用窗口函数进行注释时才进行注释的事实,通常更快。