【发布时间】:2010-10-09 20:43:11
【问题描述】:
我正在处理一个 Postgres 表(称为“lives”),其中包含带有 time_stamp、usr_id、transaction_id 和 living_remaining 列的记录。我需要一个查询,该查询将为我提供每个 usr_id 的最新 lives_remaining 总数
- 有多个用户(不同的 usr_id)
- time_stamp 不是唯一标识符:有时用户事件(表中逐行)会以相同的 time_stamp 发生。
- trans_id 仅在非常小的时间范围内是唯一的:随着时间的推移它会重复
- remaining_lives(对于给定用户)可以随时间增加和减少
示例:
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 的新手,所以我知道我需要有效地使用正确的索引。我对如何优化有点迷茫。
我发现了一个类似的讨论here。我可以执行某种与 Oracle 分析功能等效的 Postgres 类型吗?
任何关于访问聚合函数(如 MAX)使用的相关列信息、创建索引和创建更好的查询的建议将不胜感激!
附:您可以使用以下内容创建我的示例案例:
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);
【问题讨论】:
-
Josh,您可能不喜欢查询自联接等这一事实,但就 RDBMS 而言,这没关系。
-
自联接实际上最终会转换为一个简单的索引映射,其中内部 SELECT(带有 MAX 的那个)扫描索引丢弃不相关的条目,而外部 SELECT 只是抓取表中与缩小索引相对应的其余列。
-
弗拉德,感谢您的提示和解释。它让我大开眼界,了解如何开始了解数据库的内部工作原理以及如何优化查询。 Quassnoi,感谢您对主键的出色查询和提示;比尔也是。很有帮助。
-
感谢您向我展示如何获得
MAXBY2 列!
标签: sql postgresql query-optimization cbo cost-based-optimizer