SqlAlchemy mysql 毫秒或微秒精度

13

我一直在尝试使数据库中的小数时间分辨率正常工作。 我使用Python方法datetime.now()创建日期对象,然后将这些对象存储在映射到COLUMN(DATETIME(9))的字段中,该字段来自SqlAlchemy库。 最初,我遇到了我的数据被截断的错误,因为我使用的是mysql 5.5。 我已经更新到5.6.19,不再出现数据被截断的错误。

但是,数据库仍然实际上不包含小数时间条目。 例如,这是datetime.now()对象实例化时的值:

2015-04-17 16:31:12.804444
上述内容正是我所预期的。内存中的对象具有微秒级别的分辨率。现在,当它将此保存到mysql数据库后,如果我使用select语句打开mysql命令行客户端并返回该行,则会看到以下值:
2015-04-17 16:31:13

很明显,这个值被四舍五入到最近的秒。这很糟糕,我不知道是什么原因导致了这个问题!

如果有关系的话,我正在使用mysql-connector-python==2.0.3

更新: 我也尝试使用COLUMN(DATETIME(6)),但得到了相同的行为。

以下包括模型信息,以防该信息相关:

class User(Base):
        __tablename__ = 'Users'

        uid = Column(INT, primary_key=True, autoincrement=True)
        dateCreated = Column(DATETIME(6))

        def __init__(self, *args, **kwargs):
                super(user, self).__init__(*args, **kwargs)
                self.dateCreated = datetime.now()

更新: Pedro的建议并不是问题的原因,尽管它确实帮助我取得了进展,所以非常感谢。我尝试在sql连接器中逐步执行代码,直到我到达mysql插入语句。该语句确实包含小数时间值。但是,在执行时,该值会被四舍五入。当我对表进行描述时,我注意到datetime类型就是datetime,但它应该真正为datetime(6)

我使用SA模型生成数据库,其中明确定义了Column(DATETIME(6))Base.metadata.create_all(self.db, checkfirst=True),所以我不明白为什么(6)没有出现在实际的表结构中。不过我想很快就会弄清楚,并在找到答案后进行更新。

更新: DATETIME的构造函数不接受字段长度规定。它只接受时区的参数。对于我来说并不清楚如何指定datetime字段的长度,因为对于像varchar这样的类型,只需将其传递给构造函数即可。继续探索中。


当您将值选择回Python时,您会得到这个结果?使用mysql命令行工具或某个GUI工具查看它们? - abarnert
此外,根据文档,“如果给出fsp值,则其必须在0到6的范围内。值为0表示没有小数部分。如果省略,则默认精度为0。”那么,您确定DATETIME(9)不会将9视为“不在0到6的范围内,忽略并使用默认值(即0)”吗? - abarnert
另外,您能否使用其他工具插入微秒值?这将有助于排除问题是在mysql-connector-python的哪一侧。 - abarnert
4个回答

34

我遇到的问题是,标准SqlAlchemy DATETIME类不能满足mysql的要求,在构造函数中传入(6)的值以表示分数时间值。相反,需要使用sqlalchemy.dialects.mysql.DATETIME类。这个类允许使用fsp参数(分数秒参数)。所以,列声明实际上应该像这样:

dateCreated = Column(DATETIME(fsp=6)) 

感谢那些回复我的人。他们帮助我指引调查,最终发现这个玄妙而非常令人困惑的区别。


参考文档:http://docs.sqlalchemy.org/en/latest/dialects/mysql.html 搜索 sqlalchemy.dialects.mysql.DATETIME - mraxus
我实际上还没有测试过这个,但是 with_variant 可能会很有用,Column.Datetime().with_variant(mysql.DATETIME(fsp=6), 'mysql') https://docs.sqlalchemy.org/en/14/core/type_api.html#sqlalchemy.types.TypeEngine.with_variant 可能会很有用 - 这将允许仅针对 mysql 数据库进行设置。 - Charles L.

2

有没有办法知道哪个mysql驱动程序支持这个功能? - GuySoft

0
根据MySQL文档,它只支持精度高达6位(微秒):
“MySQL 5.6.4及以上版本扩展了对TIME、DATETIME和TIMESTAMP值的小数秒支持,最高可达微秒(6位数字)精度”
我不确定,但指定9个数字可能会将列还原为较小的默认精度。

0
为了在MySQL中强制使用小数秒精度,同时保持其他引擎的默认设置,您可以修改SQLAlchemy编译MySQL的DateTime类型的方式。
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.types import DateTime
from sqlalchemy.dialects.mysql import DATETIME

@compiles(DateTime, "mysql")
def compile_datetime_mysql(type_, compiler, **kw):
     return "DATETIME(6)"

这基本上是告诉SQLAlchemy仅为MySQL编译DateTime时使用自定义类型,在我们的情况下返回DATETIME(6),其中值为6的参数是fsp(来自MySQL 5.6.4的小数秒精度)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接