2013-05-01 142 views
3

我有一個postgres表與獨特日期時間字段。 我想使用/創建一個函數,它需要一個日期時間值作爲參數,並返回具有最接近的日期時間相對(但不等於)傳遞的日期時間值的行ID。第二個參數可以在之前指定或在之後指定通過的值。Postgresql - 獲取最接近的日期時間行相對於給定的日期時間值

理想情況下,本地datetime functions的某些組合可以處理此要求。否則,它必須是一個自定義函數。

問題:什麼是查詢相對日期時間的方法集合的行?

+1

特德,那麼你的問題是什麼?爲什麼你創建這個帖子,如果你「仍在工作」? – 2013-05-01 16:06:24

+2

如果有兩個記錄共享相同的日期時間值,這兩個記錄都在期望值的同一側? – wildplasser 2013-05-01 16:13:48

+0

@MaksimKolesnikov我添加了一個更清晰的問題。 – 2013-05-01 16:16:11

回答

3
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的行的時間戳列的位置低於傳遞的時間戳記的行。如果錯誤反過來。

+1

@ted。 strauss修正 – 2013-05-01 16:37:52

+0

不適用於'not positive_interval'。 – 2013-05-01 16:46:38

+0

@Igor更新爲修復 – 2013-05-01 16:55:09

0

您必須將表連接到本身,其中where條件查找基錶行的日期時間和連接的錶行的日期時間之間最小的非零(負或正)間隔。在該日期時間列上有一個索引是很好的。

P.S.你也可以查找後面的max()或min()。

1

使用簡單 - 。

假設你已經具有屬性的關鍵,和的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

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; 
+1

abs()不適用於日期時間值。 – 2013-05-01 16:37:22

+0

@ ted.strauss固定。 – 2013-05-01 16:45:02

+0

謝謝你,我希望我能把它交給你們倆。 – 2013-05-01 16:58:58

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) 

包成一個功能是留給讀者的鍛鍊; Tibial

更新:這裏是第二個與夾 - 不存在欺騙(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 
     ; 
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 

這應該是相當快上的日期時間列的索引。

+2

我認爲單獨查找第一個較高和第一個較低記錄的原則可能是在有非常大的數據集的情況下表現最好的原則,但是我認爲您需要另一個查詢級別來確定哪個是最接近的。仔細想想,通過第一個查詢在公用表格表達式中,您可以在第二個查詢中進行過濾,以僅返回比第一個發現行更接近日期的行,這可能會更有效。 – 2013-05-01 17:05:14

+0

@DavidAldridge謝謝你的建議:由於第二個參數的原因,我沒有把OP問題理解爲匹配最接近的行(儘管我可能應該有)。我不知道如何處理cte,所以我決定不去。如果它們實際上是某種在每個使用站點上覆制的宏,那麼在那裏使用它們是有意義的(並且考慮到它,如果可以在使用站點上將where子句應用於cte,那麼它可能正在工作正確的方法)。 – didierc 2013-05-01 17:22:41

相關問題