我正在处理一个 Postgres 表(称为“lives”),其中包含带有 time_stamp、usr_id、transaction_id 和 live_remaining 列的记录。我需要一个查询,该查询将为我提供每个 usr_id 的最新 lives_remaining 总数
- 有多个用户(不同的 usr_id)
- time_stamp 不是唯一标识符:有时用户事件(在表中逐行)会以相同的 time_stamp 发生。
- trans_id 仅在非常小的时间范围内是唯一的:随着时间的推移它会重复
- 剩余寿命(对于给定用户)可以随时间增加和减少
例子:
time_stamp|lives_remaining|usr_id|trans_id ----------------------------------------- 07:00 | 1 | 1 | 1 09:00 | 4 | 2 | 2 10:00 | 2 | 3 | 3 10:00 | 1 | 2 | 4 11:00 | 4 | 1 | 5 11:00 | 3 | 1 | 6 13:00 | 3 | 3 | 1
因为我需要使用每个给定 usr_id 的最新数据访问行的其他列,所以我需要一个查询,它会给出如下结果:
time_stamp|lives_remaining|usr_id|trans_id ----------------------------------------- 11:00 | 3 | 1 | 6 10:00 | 1 | 2 | 4 13:00 | 3 | 3 | 1
如前所述,每个 usr_id 都可能获得或失去生命,有时这些带时间戳的事件发生得如此接近以至于它们具有相同的时间戳!因此,此查询将不起作用:
SELECT b.time_stamp,b.lives_remaining,b.usr_id,b.trans_id FROM
(SELECT usr_id, max(time_stamp) AS max_timestamp
FROM lives GROUP BY usr_id ORDER BY usr_id) a
JOIN lives b ON a.max_timestamp = b.time_stamp
相反,我需要同时使用 time_stamp(第一)和 trans_id(第二)来识别正确的行。然后,我还需要将该信息从子查询传递到主查询,主查询将为相应行的其他列提供数据。这是我已经开始工作的黑客查询:
SELECT b.time_stamp,b.lives_remaining,b.usr_id,b.trans_id FROM
(SELECT usr_id, max(time_stamp || '*' || trans_id)
AS max_timestamp_transid
FROM lives GROUP BY usr_id ORDER BY usr_id) a
JOIN lives b ON a.max_timestamp_transid = b.time_stamp || '*' || b.trans_id
ORDER BY b.usr_id
好的,所以这行得通,但我不喜欢它。它需要一个查询中的一个查询,一个自连接,在我看来,通过抓取 MAX 发现具有最大时间戳和 trans_id 的行可以更简单。表“lives”有数千万行要解析,所以我希望这个查询尽可能快速和高效。我尤其是 RDBM 和 Postgres 的新手,所以我知道我需要有效地使用正确的索引。我对如何优化有点迷茫。
我在这里找到了类似的讨论。我可以执行某种与 Oracle 分析功能等效的 Postgres 类型吗?
任何有关访问聚合函数(如 MAX)使用的相关列信息、创建索引和创建更好的查询的建议将不胜感激!
PS您可以使用以下内容创建我的示例案例:
create TABLE lives (time_stamp timestamp, lives_remaining integer,
usr_id integer, trans_id integer);
insert into lives values ('2000-01-01 07:00', 1, 1, 1);
insert into lives values ('2000-01-01 09:00', 4, 2, 2);
insert into lives values ('2000-01-01 10:00', 2, 3, 3);
insert into lives values ('2000-01-01 10:00', 1, 2, 4);
insert into lives values ('2000-01-01 11:00', 4, 1, 5);
insert into lives values ('2000-01-01 11:00', 3, 1, 6);
insert into lives values ('2000-01-01 13:00', 3, 3, 1);