在Postgres中获取表的最后一条记录

75
我正在使用Postgres,但无法获取我的表的最后一条记录:
 my_query = client.query("SELECT timestamp,value,card from my_table");

如何知道时间戳是记录的唯一标识符,从而进行操作?

13个回答

128

如果您所说的 "last record" 是指具有最新时间戳值的记录,则可以尝试以下方法:

my_query = client.query("
  SELECT TIMESTAMP,
    value,
    card
  FROM my_table
  ORDER BY TIMESTAMP DESC
  LIMIT 1
");

4
如果没有一个插入时间的时间戳列,该怎么办? - WebWanderer
12
@WebWanderer SELECT id,value,card FROM my_table ORDER BY id DESC LIMIT 1; 翻译为:从我的表中选择id、value和card列,按照id降序排序后,选择第一条数据。 - devius
2
关键是在此之后使用“order by desc”和“limit 1”……就像上面的示例查询一样。 - Akki
如果两行具有相同的时间戳,这将返回最后插入的行吗? - user2233706
@user2233706,不一定。如果时间戳不是唯一的,则有其他选项。例如,如果有一个从序列填充的主键列,则可以选择具有最新ID的记录,CURRVAL(<序列名称>) - bpgergo

25

你可以使用

SELECT timestamp, value, card 
FROM my_table 
ORDER BY timestamp DESC 
LIMIT 1

假设你也想按 timestamp 进行排序?


20

简单方法:使用ORDER BY结合LIMIT

SELECT timestamp, value, card
FROM my_table
ORDER BY timestamp DESC
LIMIT 1;

然而,LIMIT不是标准,正如维基百科所述,SQL标准的核心功能没有明确定义空值的默认排序顺序。最后,当多个记录共享最大时间戳时,仅返回一行。

关系方式:

通常的做法是检查没有任何一行具有比我们检索到的任何一行更高的时间戳。

SELECT timestamp, value, card
FROM my_table t1
WHERE NOT EXISTS (
  SELECT *
  FROM my_table t2
  WHERE t2.timestamp > t1.timestamp
);

这是我最喜欢的解决方案,也是我倾向于使用的解决方案。缺点是一眼看去,我们的意图并不清晰。

教学方式:MAX

为了避免这个问题,可以在子查询中使用MAX代替相关查询。

SELECT timestamp, value, card
FROM my_table
WHERE timestamp = (
  SELECT MAX(timestamp)
  FROM my_table
);

但是如果没有索引,数据需要经过两次扫描才能找到解决方案,而之前的查询只需要一次扫描就能找到解决方案。也就是说,在设计查询时,除非必要,我们不应该考虑性能,因为我们可以期望优化器随着时间的推移而改进。然而,这种特殊类型的查询相当常用。

炫耀方式:窗口函数

我不建议这样做,但也许你可以给老板留下好印象或者什么的 ;-)

SELECT DISTINCT
  first_value(timestamp) OVER w,
  first_value(value) OVER w,
  first_value(card) OVER w
FROM my_table
WINDOW w AS (ORDER BY timestamp DESC);

实际上,这样做的好处是展示了一个简单查询可以用多种方式表达(我还可以想到其他几种),而选择其中一种形式应根据以下几个标准进行:

  • 可移植性(关系/教学方法)
  • 效率(关系方法)
  • 表现力(易于理解/教学方法)

9
如果您的表没有像integer auto-increment这样的Id,也没有时间戳,您仍然可以使用以下查询获取表的最后一行。
select * from <tablename> offset ((select count(*) from <tablename>)-1)

例如,这可以让您搜索更新的平面文件,找到/确认以前版本的结束位置,并将剩余的行复制到您的表中。

如果表没有行,这将抛出 2201X: OFFSET 必须不是负数 - Andrus

8
假设您的主键为“id”,则可以使用以下方法查询最后插入的记录:
SELECT timestamp,value,card FROM my_table WHERE id=(select max(id) from my_table)

假设每插入一行新数据都会使用该表ID的最高整数值。


7

如果您接受小费,请在此表格中创建一个序列号ID。该字段的默认值为:

nextval('table_name_field_seq'::regclass).

因此,您使用查询调用最后注册。使用您的示例:
pg_query($connection, "SELECT currval('table_name_field_seq') AS id;

我希望这个技巧能够帮助您。


抱歉,由于我的英语不好。 - phsaires
1
如果最后一行被删除,这仍将返回其ID... 不是一个安全的提示 :) - ALTN
2
当前会话中返回最近通过nextval获取的此序列的值。因此,如果nextval未在当前会话中调用,则不仅会返回错误,而且您也无法看到其他会话中所做的更改。 - Łukasz Kamiński

6
获取最后一行:
按排序顺序获取最后一行:如果表中有指定时间/主键的列,
1. 使用 LIMIT 子句
SELECT * FROM USERS ORDER BY CREATED_TIME DESC LIMIT 1;
2. 使用 FETCH 子句 - 参考 SELECT * FROM USERS ORDER BY CREATED_TIME FETCH FIRST ROW ONLY;
按行插入顺序获取最后一行:如果表中没有指定时间/任何唯一标识符的列,
3. 使用 CTID 系统列,其中 ctid 表示表中行的物理位置 - 参考 SELECT * FROM USERS WHERE CTID = (SELECT MAX(CTID) FROM USERS);
考虑以下表,
userid |username |    createdtime |
     1 |       A |  1535012279455 |
     2 |       B |  1535042279423 | //as per created time, this is the last row
     3 |       C |  1535012279443 |
     4 |       D |  1535012212311 |
     5 |       E |  1535012254634 | //as per insertion order, this is the last row

查询1和2返回,
userid |username |    createdtime |
     2 |       B |  1535042279423 |

当3返回时,

userid |username |    createdtime |
     5 |       E |  1535012254634 |

注意:在更新旧行时,它会删除旧行并更新数据,并将其作为新行插入表中。因此,使用以下查询返回最新进行数据修改的元组。
现在更新一行,使用 UPDATE USERS SET USERNAME = 'Z' WHERE USERID='3'
表格变成这样,
userid |username |    createdtime |
     1 |       A |  1535012279455 |
     2 |       B |  1535042279423 |
     4 |       D |  1535012212311 |
     5 |       E |  1535012254634 |
     3 |       Z |  1535012279443 |

现在查询3返回,
userid |username |    createdtime |
     3 |       Z |  1535012279443 |

4
使用以下内容:
SELECT timestamp, value, card 
FROM my_table 
ORDER BY timestamp DESC 
LIMIT 1

1
这些都是不错的答案,但如果您想使用聚合函数来获取由任意查询生成的结果集中的最后一行,有一种标准方法可以做到这一点(取自Postgres wiki,但应该适用于符合SQL标准的十年前或更早版本的任何内容):
-- Create a function that always returns the last non-NULL item
CREATE OR REPLACE FUNCTION public.last_agg ( anyelement, anyelement )
RETURNS anyelement LANGUAGE SQL IMMUTABLE STRICT AS $$
        SELECT $2;
$$;

-- And then wrap an aggregate around it
CREATE AGGREGATE public.LAST (
        sfunc    = public.last_agg,
        basetype = anyelement,
        stype    = anyelement
);

如果您有合理的排序,通常最好执行select ... limit 1,但如果您需要在聚合内执行此操作并希望避免子查询,则此方法很有用。
另请参见this question,这是一个自然答案的情况。

0
对于最后一条记录,您可以使用以下id:
SELECT * FROM table_name ORDER BY id DESC LIMIT 1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接