简单查询
根据初始问题,第一个示例使用 date 列。
不确定 Ruby 语法,但正确的 SQL 语句应该是:
SELECT *
FROM tbl
ORDER BY @(date_col - '2012-12-29'::date)
@ being the "absolute value" operator.
切勿使用date 或time 作为标识符。虽然在 PostgreSQL 中被允许(有一些限制),但它们是 reserved words in the SQL standard,它会导致令人困惑的错误消息和可能的意外错误。
卓越的性能
根据评论中的更新,其余部分与 timestamp 列一起使用。
对于小型表或即席查询,上述解决方案就可以了。对于中型或大型表,如果性能很重要,我建议采用更复杂的方法。
Condicio sine qua non 是date 或timestamp 列上的索引。像这样:
CREATE INDEX tbl_my_timestamp_idx ON tbl(my_timestamp);
在索引到位后,以下查询将影响简单查询对较大表的性能:
SELECT *
FROM (
(
SELECT *
FROM tbl
WHERE my_timestamp >= '2012-12-30 11:32'::timestamp
ORDER BY my_timestamp
LIMIT 3
)
UNION ALL
(
SELECT *
FROM tbl
WHERE my_timestamp < '2012-12-30 11:32'::timestamp
ORDER BY my_timestamp DESC
LIMIT 3
)
) x
ORDER BY @extract('epoch' FROM (my_timestamp - '2012-12-28 11:32'::timestamp))
LIMIT 3;
怎么会?
第一个查询使用表达式作为条件。 Postgres 必须为每一行计算一个值,然后按结果排序并选择前几行。小桌子没问题,但大桌子非常昂贵。 O(n); n 是表中的行数。它不能使用普通索引。加上在所有行中排序和挑选获胜者的一些不小的成本。
您可以在表达式上创建一个索引,这将是最快的,但这只适用于一个恒定的时间戳来比较 - 这几乎不是一个现实的用例。
第二个查询根据您在索引中的时间戳找到位置,顺序读取接下来几行的元组指针并直接从表中获取它们(或者甚至直接从索引中获取,在 9.2 中使用仅索引扫描)。两次,一次上升,一次下降,因为我们不知道同行如何比较。但这只是 2 x O(log(n)) (typical b-tree look-up cost) 仅对少数预先选择的行进行计算。从小样本中挑选获胜者的成本不变。
只需使用EXPLAIN ANALYZE 进行测试。在对真实表格的快速测试中,我得到了 1000 倍的 50k 行表格。并且它不断扩大以适应更大的桌子。