2017-03-02 89 views
1

他們是否有任何簡單(快速)的方式來獲取表並找到距離SQLAlchemy中給定dateTime最近的日期?在大多數情況下,增量將在給定的日期和表中的日期之間變成秒。SQLAlchemy最近的日期時間

日期列是主鍵

編輯: 我使用SQLite

回答

3

類似的東西既然是主鍵的簡單的「排序DIFF上升,取第1行」可能不是最快的,但簡單的解決方案。一種快速和骯髒的方式可能是獲取大於和小於給定日期時間的聯合,按升序和降序排序並限於第一行,然後從第二行中選擇具有較小差異的聯合。例如使用PostgreSQL作爲後端,並用一年的時間戳記與12S分辨率的測試表:

sopython=> create table testi (key timestamp without time zone primary key); 
CREATE TABLE 
sopython=> insert into testi 
select * 
from generate_series(now() at time zone 'utc' - '1 year'::interval, 
        now() at time zone 'utc', 
        '12 seconds'::interval); 
INSERT 0 2628001 

和Python的:

In [29]: from sqlalchemy import union_all, case 

In [30]: from sqlalchemy.orm import aliased 

In [31]: the_time = datetime(2016, 5, 5, 10, 45, 55) 

創建取最接近值工會,包裹在子查詢一個SELECT語句,以便它例如,也可以在SQLite中工作。

In [32]: greater = session.query(Testi).filter(Testi.key > the_time).\ 
    ...:  order_by(Testi.key.asc()).limit(1).subquery().select() 

In [33]: lesser = session.query(Testi).filter(Testi.key <= the_time).\ 
    ...:  order_by(Testi.key.desc()).limit(1).subquery().select() 

In [34]: the_union = union_all(lesser, greater).alias() 

別名模型到工會

In [35]: testi_alias = aliased(Testi, the_union) 

的結果從給定的日期時間

In [36]: the_diff = testi_alias.key - the_time 

或SQLite中

In [36]: the_diff = func.julianday(testi_alias.key) - func.julianday(the_time) 

取計算的差越接近的2 se的怪異度是在Postgresql中間隔的getting the absolute value。其他DB需要不同的解決方案進行差值計算並取絕對值。用SQLite只需func.abs(the_diff)

In [37]: session.query(testi_alias).\ 
    ...:  order_by(case([(the_diff < timedelta(0), -the_diff)], 
    ...:     else_=the_diff)).\ 
    ...:  first() 
Out[37]: <sqlalchemy.ext.automap.testi at 0x7f096f837828> 

In [38]: _.key 
Out[38]: datetime.datetime(2016, 5, 5, 10, 45, 54, 855799) 

雖然只是DIFF訂購和限制的簡單的解決方案在某些800毫秒跑這臺機器上,上面的查詢完成約70-100ms。如果您將數據翻倍,那麼簡單的解決方案 - 依賴於seq掃描 - 也是雙倍的。

工會從表中尋找這兩個值:

In [14]: session.query(testi_alias.key).all() 
Out[14]: 
[(datetime.datetime(2016, 5, 5, 10, 45, 54, 855799)), 
(datetime.datetime(2016, 5, 5, 10, 46, 6, 855799))] 

最後,你可以在一個通用的功能包了這一切:

def get_closest(session, cls, col, the_time): 
    greater = session.query(cls).filter(col > the_time).\ 
     order_by(col.asc()).limit(1).subquery().select() 

    lesser = session.query(cls).filter(col <= the_time).\ 
     order_by(col.desc()).limit(1).subquery().select() 

    the_union = union_all(lesser, greater).alias() 
    the_alias = aliased(cls, the_union) 
    the_diff = getattr(the_alias, col.name) - the_time 
    abs_diff = case([(the_diff < timedelta(0), -the_diff)], 
        else_=the_diff) 

    return session.query(the_alias).\ 
     order_by(abs_diff.asc()).\ 
     first() 

get_closest(session, Testi, Testi.key, the_time) 
+0

是使用select * from表where datetime> your_date_time的限制1; select * from table where datetime Timo

+0

我會得到該行還是時間值? – Timo

+0

2選擇是解決方案背後的想法,但不是完整的解決方案。請注意,如果最小差異爲0,那麼特定的查詢將不會返回正確的結果,因爲這兩個查詢都不相等。您可以調整查詢以返回任一個。 –

0

用普通的SQL,你可以這樣做:

select * from table where datetime > your_date_time limit 1; 
select * from table where datetime < your_date_time limit 1; 

拿到第一後和首先在你的日期時間之前,然後計算差異並得到最接近的。

隨着SQLAlchemy的你也許可以寫使用.limit.filter方法

+0

我需要符合最近的時間,這行不僅是時間 – Timo

+0

'select *'返回完整的行,不僅時間 – Gianluca