4

我有一个带有唯一日期时间字段的 postgres 表。我想使用/创建一个函数,该函数将日期时间值作为参数,并返回与传递的日期时间值最接近的日期时间相对(但不等于)的行 ID。第二个参数可以在传递值之前之后指定。

理想情况下,本机日期时间函数的某种组合可以处理此要求。否则它必须是一个自定义函数。

问题:查询一组行的相对日期时间的方法是什么?

4

6 回答 6

4
select id, passed_ts - ts_column difference
from t
where
    passed_ts > ts_column and positive_interval
    or
    passed_ts < ts_column and not positive_interval
order by abs(extract(epoch from passed_ts - ts_column))
limit 1

passed_ts是时间戳参数,positive_interval是一个布尔参数。如果为 true,则仅时间戳列低于传递的时间戳的行。如果为假,则相反。

于 2013-05-01T16:25:55.920 回答
1

您希望 select 语句的第一行以降序(或升序)顺序生成低于(或高于)给定日期时间的所有行。

函数体的伪代码:

SELECT id
FROM table
WHERE IF(@above, datecol < @param, datecol > @param)
ORDER BY IF (@above. datecol ASC, datecol DESC)
LIMIT 1

但是,这是行不通的:不能以排序方向为条件。

第二个想法是做两个查询,然后选择:

SELECT *
FROM (
    (
    SELECT 'below' AS dir, id
    FROM table
    WHERE datecol < @param
    ORDER BY datecol DESC
    LIMIT 1
    ) UNION (
    SELECT 'above' AS dir, id
    FROM table
    WHERE datecol > @param
    ORDER BY datecol ASC
    LIMIT 1)
 ) AS t
 WHERE dir = @dir

使用日期时间列上的索引应该很快。

于 2013-05-01T16:41:59.437 回答
1
-- test rig
DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;

CREATE TABLE lutser
        ( dt timestamp NOT NULL PRIMARY KEY
        );
-- populate it
INSERT INTO lutser(dt)
SELECT gs
FROM generate_series('2013-04-30', '2013-05-01', '1 min'::interval) gs
        ;
DELETE FROM lutser WHERE random() < 0.9;

--

-- The query:
WITH xyz AS (
        SELECT dt AS hh
        , LAG (dt) OVER (ORDER by dt ) AS ll
        FROM lutser
        )
SELECT  *
FROM xyz bb
WHERE '2013-04-30 12:00' BETWEEN bb.ll AND bb.hh
        ;

结果:

NOTICE:  drop cascades to table tmp.lutser
DROP SCHEMA
CREATE SCHEMA
SET
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "lutser_pkey" for table "lutser"
CREATE TABLE
INSERT 0 1441
DELETE 1288
         hh          |         ll          
---------------------+---------------------
 2013-04-30 12:02:00 | 2013-04-30 11:50:00
(1 row)

将其包装成一个函数留给读者作为练习

更新:这是第二个带有夹层不存在技巧(TM)的:

SELECT lo.dt AS ll
FROM lutser lo
JOIN lutser hi ON hi.dt > lo.dt
        AND NOT EXISTS (
        SELECT * FROM lutser nx
        WHERE nx.dt < hi.dt
        AND nx.dt > lo.dt
        )
WHERE '2013-04-30 12:00' BETWEEN lo.dt AND hi.dt
        ;
于 2013-05-01T16:38:35.487 回答
1

简单地使用-。

假设您有一个带有属性 Key、Attr 和 T 的表(带有或不带有时区的时间戳):

 you can search with

 select min(T - TimeValue) from Table where (T - TimeValue) > 0;

这会给你主要的区别。您可以将此值与同一个表的连接结合起来,以获得您感兴趣的元组:

 select * from (select *, T - TimeValue as diff from Table) as T1 NATURAL JOIN
               ( select min(T - TimeValue) as diff from Table where (T - TimeValue) > 0) as T2;

应该这样做

--dmg

于 2013-05-01T16:21:32.340 回答
0

尝试类似:

SELECT * 
FROM your_table
WHERE (dt_time > argument_time and search_above = 'true')
   OR (dt_time < argument_time and search_above = 'false')
ORDER BY CASE WHEN search_above = 'true' 
              THEN dt_time - argument_time
              ELSE argument_time - dt_time
         END
LIMIT 1;
于 2013-05-01T16:29:29.200 回答
0

您必须使用 where 条件将表连接到自身,以查找基表行的日期时间和连接表行的日期时间之间的最小非零(负或正)间隔。在该日期时间列上有一个索引会很好。

PS您还可以查找前一个的 max() 或后续的 min()。

于 2013-05-01T16:19:21.927 回答