【问题标题】:Best way to store python datetime.time in a sqlite3 column?将 python datetime.time 存储在 sqlite3 列中的最佳方法?
【发布时间】:2014-12-24 18:21:22
【问题描述】:

我正在尝试用 python + sqlite3 替换我对 SAS 的使用;我正在尝试将我的数据从 SAS 数据集移动到 SQLite 数据库。我有许多在 python 中正确表示为 datetime.time 对象的时间字段。由于 SQLite 是“轻输入”的,我正在寻找有关在列中存储时间的格式的建议。 (我知道我必须编写 python 适配器等来读写列中的对象。)这些是我需要考虑的特性:

  • SQLite 处理查询中的列的能力。 (例如,我可以选择出现在两次之间的行吗?)
  • 字段的大小。 (我的表通常有数亿行。)
  • 人类可读性。 (我正在考虑将时间存储为整数:自午夜以来的微秒。但这使得观察数据变得更加困难。)

有没有人满意地解决了这个问题?

【问题讨论】:

    标签: python datetime sqlite


    【解决方案1】:

    在 sqlite 表中存储任何可序列化的 Python 对象都有一个通用方法。

    以下是 datetime.time 对象的代码外观:

    import sqlite3
    import datetime as DT
    
    def adapt_timeobj(timeobj):
        return ((3600*timeobj.hour + 60*timeobj.minute + timeobj.second)*10**6 
                + timeobj.microsecond)
    
    def convert_timeobj(val):
        val = int(val)
        hour, val = divmod(val, 3600*10**6)
        minute, val = divmod(val, 60*10**6)
        second, val = divmod(val, 10**6)
        microsecond = int(val)
        return DT.time(hour, minute, second, microsecond)
    
    
    # Converts DT.time to TEXT when inserting
    sqlite3.register_adapter(DT.time, adapt_timeobj)
    
    # Converts TEXT to DT.time when selecting
    sqlite3.register_converter("timeobj", convert_timeobj)
    
    con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES)
    cur = con.cursor()
    
    # declare timecol to be of type timeobj
    cur.execute("create table test (timecol timeobj)")
    
    cur.executemany("insert into test (timecol) values (?)", 
                    [(DT.time(1,2,3,4), ), (DT.time(5,6,7,8),) ])
    

    您可以在 SQL 中使用不等式,但请注意,要比较的值是 adapt_timeobj 返回的值,不是 datetime.time 对象。幸运的是,如果adapt_timeobj 函数返回的整数可以按照与对应的datetime.time 对象相同的顺序进行排序(如上所述),那么 SQL 中的不等式将按需要工作。

    cur.execute("select timecol from test where timecol < ?",
                [DT.time(4,5,6)])
    print(cur.fetchall())
    # [(datetime.time(1, 2, 3, 4),)]
    
    cur.execute("select timecol from test where timecol < ?",
                [DT.time(8,0,0)])
    print(cur.fetchall())
    # [(datetime.time(1, 2, 3, 4),), (datetime.time(5, 6, 7, 8),)]
    
    con.commit()
    cur.close()
    con.close()
    

    注意:如果您查看编辑历史记录,您会看到adapt_timeobjconvert_timeobj 的更简单替代方案,它将数据存储为str,而不是int。它更简单,但将数据存储为int 更快且内存效率更高。

    【讨论】:

    • 值得一提的是,在这种情况下传递PARSE_DECLTYPES不是可选的。
    • @J.F.Sebastian:也可以使用PARSE_COLNAMES,但是你需要使用像SELECT timecol [timeobj] ...这样的SQL
    • 我在其他情况下(不是 python + sqlite)从午夜开始使用微秒。我不喜欢它,但它是明智的。我不知道像您在示例中那样在查询中使用 python 对象的能力。我可能会从你的建议开始。谢谢。
    • @jdmarino:我在第一篇文章中犯了一个错误。 SQL 查询没有使用 python 对象。比较是在基础数据库值上完成的——adapt_timeobj 返回的值类型。
    【解决方案2】:

    我真的很喜欢@unutbuanswer,但这里有一个简单的方法来存储时间戳。

    RFC 3339 是一种非常明确的时间戳格式,易于计算机解析,易于人类阅读。您可以将时间戳存储为字符串。

    RFC 3339 的一个很好的特性:简单的 ASCII 排序也按时间顺序排序。

    但您并不真正需要规范,因为它非常简单。这是一个例子:

    2014-12-24T23:59:59.9999-08:00
    

    这是我所在时区圣诞节前的最后一秒,比 UTC 晚 8 小时(因此是 -08:00 部分)。年、月、日、字符串T、时、分、秒、可选小数秒、时区。

    时区也可以是Z,表示UTC时间。但是将时间存储在本地时区可能更方便,这样您就可以更轻松地阅读它们。

    【讨论】:

    • rfc3339 需要日期部分。 OP 只询问datetime.time() - 没有日期。此外,-08 不正确。应该是-08:00
    • 好吧,这个问题并没有禁止存储日期,我认为 RFC 3339 有一些优势。您对时区的看法是正确的,我已修复它;谢谢。
    • 感谢您的回答。我的数据集并不总是有日期,只有时间。我已经考虑了您的解决方案,但我需要添加一个“标准日期”作为占位符(例如 1/1/1960)。这种方法的好处是排序(正如您所指出的),而且我可以使用 sqlite 的日期处理功能。缺点是我代表了一个我实际上没有的日期。
    • 当你只需要时间的时候,我不会去添加一个假的日期。但是您可能希望仅使用 RFC 3339 中的时间部分进行标准化:HH:MM:SS.fraction 其中 HH 是从 00 到 23 的两位数小时,MM 和 SS 始终是两位数(00 到 59),并且fraction 是可选的,但如果存在则表示一秒的小数部分的浮点值。易于阅读,而且 ASCII 排序是按时间排序的。
    猜你喜欢
    • 2011-09-07
    • 2021-06-19
    • 2020-12-07
    • 2012-07-01
    • 2021-11-30
    • 2019-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多