是否有postgres CLOSEST运营商?

时间:2011-05-23 20:46:16

标签: sql postgresql

我正在寻找一些东西,如下表所示:

| id | number |
|  1 |     .7 |
|  2 |   1.25 |
|  3 |   1.01 |
|  4 |    3.0 |

查询SELECT * FROM my_table WHERECLOSEST(1)将返回第3行。我只关心数字。现在我有一个程序只是循环遍历每一行并进行比较,但我认为信息应该可以从b树索引获得,所以这可能是一个内置的,但我找不到任何文件表明它确实存在。

4 个答案:

答案 0 :(得分:20)

我可能对语法有点偏僻,但是这个参数化查询(所有?原始问题的'1')应该运行得很快,基本上是2个B-Tree查找[假设数字被索引]。

SELECT * FROM
(
  (SELECT id, number FROM t WHERE number >= ? ORDER BY number LIMIT 1) AS above
  UNION ALL
  (SELECT id, number FROM t WHERE number < ? ORDER BY number DESC LIMIT 1) as below
) 
ORDER BY abs(?-number) LIMIT 1;

使用~5e5行(索引在number上)的表的查询计划如下所示:

psql => explain select * from (
        (SELECT id, number FROM t WHERE number >= 1 order by number limit 1) 
        union all
        (select id, number from t where number < 1 order by number desc limit 1)
) as make_postgresql_happy 
order by abs (1 - number) 
limit 1;
                                                  QUERY PLAN
--------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.24..0.24 rows=1 width=12)
   ->  Sort  (cost=0.24..0.24 rows=2 width=12)
         Sort Key: (abs((1::double precision - public.t.number)))
         ->  Result  (cost=0.00..0.23 rows=2 width=12)
               ->  Append  (cost=0.00..0.22 rows=2 width=12)
                     ->  Limit  (cost=0.00..0.06 rows=1 width=12)
                           ->  Index Scan using idx_t on t  (cost=0.00..15046.74 rows=255683 width=12)
                                 Index Cond: (number >= 1::double precision)
                     ->  Limit  (cost=0.00..0.14 rows=1 width=12)
                           ->  Index Scan Backward using idx_t on t  (cost=0.00..9053.67 rows=66136 width=12)
                                 Index Cond: (number < 1::double precision)
(11 rows)

答案 1 :(得分:5)

您可以尝试这样的事情:

select *
from my_table
where abs(1 - number) = (select min(abs(1 - number)) from t)

这与手动循环表没有多大区别,但至少它允许数据库在“数据库空间”内进行循环,而不必在函数和数据库内部之间来回跳转。此外,将它全部推送到单个查询中可以让查询引擎知道您正在尝试做什么,然后它可以尝试以合理的方式执行此操作。

答案 2 :(得分:3)

第二个答案是正确的,但我在&#34; UNION ALL&#34;

上遇到错误

<强> DBD::Pg::st execute failed: ERROR: syntax error at or near "UNION"

我用这段代码修复了它:

SELECT * FROM
  (
    (SELECT * FROM table WHERE num >= ? ORDER BY num LIMIT 1)
        UNION ALL
    (SELECT * FROM table WHERE num < ?  ORDER BY num DESC LIMIT 1)
  ) as foo
ORDER BY abs(?-num) LIMIT 1;

诀窍是从内部表中删除AS并仅在UNION上使用它。

答案 3 :(得分:0)

如果您希望在组中找到最接近的值,此代码非常有用。在这里,我根据tb列接近目标值 0.5 的距离,将column_you_wish_to_group_by分割为val

SELECT *
FROM (
  SELECT
    ROW_NUMBER() OVER (PARTITION BY t.column_you_wish_to_group_by ORDER BY abs(t.val - 0.5) ASC) AS r,
    t.*
  FROM
    tb t) x 
WHERE x.r = 1;