PostgreSQL:通过加入2个表来获取最新更新

时间:2013-09-17 10:17:53

标签: sql postgresql join

我需要连接2个表,以根据第1个表中的有效行获取第2个表中的最后/最新更新。

以下代码就是一个例子。

表1:注册用户
该表包含在系统中注册的用户列表。 当用户注册时,它将被添加到此表中。用户注册了名称和注册时间。 用户可以从系统中取消注册。完成此操作后,取消注册列将更新为删除用户的时间。如果此值为NULL,则表示用户仍处于注册状态。

CREATE TABLE users (
    entry_idx   SERIAL PRIMARY KEY,
    name        TEXT NOT NULL,
    reg_time    TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    dereg_time  TIMESTAMP WITH TIME ZONE DEFAULT NULL  
);

表2:用户更新
该表包含用户的更新。每次用户更改属性(示例位置)时,更改都会存储在此表中。由于需要在表中保留历史记录,因此不必删除任何更新。

CREATE TABLE user_updates (
    entry_idx   SERIAL PRIMARY KEY,
    name        TEXT NOT NULL,
    position    INTEGER NOT NULL,
    time        TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

所需输出
因此,鉴于上述信息,我需要获得一个仅包含当前注册用户的最新更新的新表。

测试数据
以下数据可用作上表的测试数据:

-- Register 3 users
INSERT INTO users(name) VALUES ('Person1');
INSERT INTO users(name) VALUES ('Person2');
INSERT INTO users(name) VALUES ('Person3');
-- Add some updates for all users
INSERT INTO user_updates(name, position) VALUES ('Person1', 0);
INSERT INTO user_updates(name, position) VALUES ('Person1', 1);
INSERT INTO user_updates(name, position) VALUES ('Person1', 2);
INSERT INTO user_updates(name, position) VALUES ('Person2', 1);
INSERT INTO user_updates(name, position) VALUES ('Person3', 1);
-- Unregister the 2nd user
UPDATE users SET dereg_time = NOW() WHERE name = 'Person2';

从上面,我想要人1和人3的最后更新。

尝试失败
我尝试过使用连接和其他方法,但结果并不是我想要的。问题几乎与提问here的问题相同。我在答案1中使用了解决方案,它确实给出了正确的答案,但是在我的系统中得到的答案也需要很长时间。

根据以上链接,我创建了以下“工作”的查询:

SELECT
  t1.*
  , t2.*
FROM
  users t1
JOIN (
  SELECT
      t.*,
      row_number()
  OVER (
      PARTITION BY
        t.name
      ORDER BY t.entry_idx DESC
      ) rn
  FROM user_updates t
  ) t2
ON
  t1.name = t2.name
AND
  t2.rn = 1
WHERE
  t1.dereg_time IS NULL;

问题 上述查询的问题是需要很长时间才能完成。表1包含一小部分用户,而表2包含大量更新。我认为查询处理2个表的方式可能效率低(基于我对查询的有限理解)。从pgAdmin的解释来看,它在加入注册表之前对更新1进行了大量的排序和聚合。

问题
如何制定查询以高效快速地获取注册用户的最新更新?

2 个答案:

答案 0 :(得分:1)

PostgreSQL对此类查询有一种特殊的distinct on语法:

select distinct on(t1.name)
--it's better to specify columns explicitly, * just for example
    t1.*, t2.*           
from users as t1
    left outer join user_updates as t2 on t2.name = t1.name
where t1.dereg_time is null
order by t1.name, t2.entry_idx desc 

<强> sql fiddle demo

你可以尝试一下,但对我来说你的查询也可以正常工作。

答案 1 :(得分:0)

我使用q1来获取每个用户的上次更新。然后与用户联接以删除已取消注册的条目。然后加入q2以获取其余的user_update字段。

select users.*,q2.* from users
join
(select name,max(time) t from user_updates group by name) q1
on users.name=q1.name 
join user_updates q2 on q1.t=q2.time and q1.name=q2.name
where
users.dereg_time is null

(我没有测试过。已经编辑了一些东西)