1

我想创建一个范围,从最近的日期到最远的日期对数据进行排序。

例如,我有 3 个值:

<Value id: 1, date: '2012-12-20'>
<Value id: 2, date: '2012-12-28'>
<Value id: 3, date: '2012-12-31'>

然后我想对最接近给定日期的日期进行排序:2012-12-29.
结果我应该有这个命令:2, 3, 1
如果我选择2012-12-30结果必须是:3, 2, 1.

我试过这样的事情:

scope :order_by_closest_date, lambda{|time| 
  select("*, (date - DATE('#{time}')) AS time").order("time ASC")
}

但它不起作用。
有关信息:Rails 3.2.9 Ruby 1.9.3 Postgresql 9.1.4。
有任何想法吗?

4

2 回答 2

2

简单查询

第一个示例date根据初始问题使用列。

不确定 Ruby 语法,但正确的 SQL 语句应该是:

SELECT * 
FROM   tbl
ORDER  BY @(date_col - '2012-12-29'::date)

@是“绝对值”运算符。

永远不要使用dateortime作为标识符。虽然在 PostgreSQL 中被允许(有一些限制),但这些是SQL 标准中的保留字,它会导致令人困惑的错误消息和可能的意外错误。

杰出的表现

timestamp其余部分根据评论中的更新与一列一起使用。

对于小型表或临时查询,上述解决方案就可以了。对于中型或大型表,如果性能很重要,我建议采用更复杂的方法。

Condicio sine qua non 是or列上的索引。像这样:datetimestamp

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;
  • UNION ALL-的两条腿周围的括号SELECT不是可选的。需要适用LIMIT于每条腿。

  • 如果您按附加列排序,请在索引中反映这一点 - 在这种情况下使用多列索引

怎么会这样?

第一个查询使用表达式作为条件。Postgres 必须为每一行计算一个值,然后按结果排序并选择前几行。小桌子没问题,但大桌子非常昂贵。O(n) ; n是表中的行数。它不能使用普通索引。加上在所有行中排序和挑选获胜者的一些不小的成本。
可以在表达式上创建一个索引,这将是最快的,但这仅适用于比较恒定的时间戳 - 这几乎不是一个实际的用例。

第二个查询根据您在索引中的时间戳找到位置,顺序读取接下来几行的元组指针,并直接从表中获取它们(或者甚至直接从索引中获取,在 9.2 中使用仅索引扫描)。两次,一次上升,一次下降,因为我们不知道同行如何比较。但这只是2 x O(log(n))典型的 b-tree 查找成本)计算仅针对少数预先选择的行进行。从小样本中挑选获胜者的成本是微不足道的。

只需测试EXPLAIN ANALYZE. 在对真实表格的快速测试中,我得到了1000 的因子,表格有 50k 行。并且它不断扩大以适应更大的桌子。

于 2012-12-29T13:00:30.270 回答
0

尝试

scope :order_by_closest_date, lambda{|time| 
  select("*, DATEDIFF(date,DATE('#{time}')) AS time").order("time ASC")
}
于 2012-12-29T12:51:12.333 回答