【问题标题】:Filter sqlalchemy sqlite datetime column by time按时间过滤 sqlalchemy sqlite datetime 列
【发布时间】:2016-04-23 01:22:20
【问题描述】:

我不确定如何仅使用时间字段来过滤我的数据库。现在我有一个类叫DatabasePolgygon

class DatabasePolygon(dbBase):
    __tablename__ = 'objects'

    begin_time = Column(DateTime) # starting time range of shape
    end_time = Column(DateTime) # ending time range of shape
    # Other entries not relevant to this question

begin_time 和 end_time 可能等于2006-06-01 14:45:23 等值,它们表示对象(在本例中为绘图上的形状)覆盖的 X 轴范围。我想允许对我的用户进行高级搜索,特别是询问在一段时间内出现的所有对象。但是,如何使用 DateTime 字段完成此操作?

        # Grab all shapes that appear above this certain time
        query_result = query_result.filter(
            DatabasePolygon.begin_time >= datetime.strptime(rng['btime']), %H:%M:%S')
        )

问题是我将带有Y-m-d H-M-S 的日期时间对象与仅带有H-M-S 的对象进行比较。一个示例场景是,如果用户想要所有出现在 14:45:24 范围之外的对象,无论年/月/日如何,因此我们将拥有 rng['btime']=14:45:24begin_time=2006-06-01 14:45:23,这似乎并不实际比较时过滤任何内容。

有什么方法可以有效地比较这列数据中的时间吗?我希望能够做类似的事情

        # Grab all shapes that appear above this certain time
        query_result = query_result.filter(
            DatabasePolygon.begin_time.time() >= datetime.strptime(rng['btime']), %H:%M:%S').time()
        )

【问题讨论】:

  • 底层数据库索引在日期时间列上的工作方式意味着没有有效的方法来做到这一点,除非一些非常具体的事情是真的。例如。如果您的数据跨越的天数很少,那么您可以对每天的时间范围进行单独的有效查询。除此之外,我认为你最好的选择是将所有数据拉到 python 然后过滤。一些数据库支持功能索引——这可以让你做你想做的事,但我不知道 sqlalchemy 是否支持它们。
  • 进一步评论来自@Tom re: 索引 - SQLite 在版本 3.9.0 中增加了对表达式索引的支持(​​参考:here)所以如果 SQLAlchemy 产生适当的 SQL 查询,那么 SQLite 应该能够有效地处理它们。
  • @GordThompson 不太清楚在这种情况下如何理解索引,如果您打算回答,能否提供一些代码?
  • sqlite.org/expridx.html - 在您的情况下,表达式仅将日期时间列转换为时间部分(然后对其进行索引)。

标签: python sqlite datetime sqlalchemy


【解决方案1】:

这似乎是可能的,但需要满足一些条件。

 
目标 1:做到(完全)。

使用名为Thing 的类来保存“对象”表中的idbegin_time 值:

class Thing(Base):
    __tablename__ = 'objects'

    id = Column(Integer, primary_key=True)
    begin_time = Column(DateTime)

    def __repr__(self):
       return "<Thing(id=%d, begin_time='%s')>" % (self.id, self.begin_time)

并在 SQLite 数据库的“objects”表中测试数据

id  begin_time
--  -------------------
 1  1971-01-14 17:21:53
 2  1985-05-24 10:11:12
 3  1967-07-01 13:14:15

不幸的是,这不起作用:

engine = create_engine(r'sqlite:///C:\__tmp\test.db', echo=True)

Session = sessionmaker(bind=engine)
session = Session()
for instance in session.query(Thing)\
        .filter(Thing.begin_time[11:]<'17:00:00')\
        .order_by(Thing.id):
    print(instance)

生产

NotImplementedError:此表达式不支持运算符“getitem”

但是,这确实有效...

engine = create_engine(r'sqlite:///C:\__tmp\test.db', echo=True)

conn = engine.connect()
result = conn.execute("SELECT id FROM objects WHERE substr(begin_time,12)<'17:00:00'")
id_list = [row[0] for row in result.fetchall()]
result.close()
conn.close()

Session = sessionmaker(bind=engine)
session = Session()
for instance in session.query(Thing)\
        .filter(Thing.id.in_(id_list))\
        .order_by(Thing.id):
    print(instance)

 
目标 2:高效

控制台输出告诉我们第一个 SELECT 确实是

SELECT id FROM objects WHERE substr(begin_time,12)<'17:00:00'

如果我们使用 SQLite 3.9.0 或更高版本并创建了“表达式索引”

CREATE INDEX time_idx ON objects(substr(begin_time,12));

那么 SQLite 将能够避免表扫描。不幸的是,即使是目前最新版本的 CPython 2.7 (2.7.11) 仍然附带一个太旧的 sqlite3 模块

Python 2.7.11 (v2.7.11:6d1b6a68f775, Dec  5 2015, 20:32:19) [MSC v.1500 32 bit (Intel)] on win32
>>> import sqlite3
>>> sqlite3.sqlite_version
'3.6.21'

这样索引不能存在于数据库中,否则 SQLAlchemy 会阻塞它:

sqlalchemy.exc.DatabaseError: (sqlite3.DatabaseError) 格式错误的数据库架构 (time_idx) - 靠近 "(": 语法错误 [SQL: "SELECT id FROM objects WHERE substr(begin_time,12)

所以,如果“高效”部分真的很重要,那么您可能需要说服 Python 使用更新版本的 SQLite。可以在问题中找到一些指导

Force Python to forego native sqlite3 and use the (installed) latest sqlite3 version

【讨论】:

    猜你喜欢
    • 2020-08-25
    • 2019-10-27
    • 2018-12-29
    • 2014-04-21
    • 2017-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多