【问题标题】:Rails / MySQL: How to query for a record by its full 'created_at' timestamp?Rails / MySQL:如何通过完整的“created_at”时间戳查询记录?
【发布时间】:2020-08-07 13:36:50
【问题描述】:

我希望能够通过created_at 时间戳(在本例中为:2020-08-07 11:30:28.5934908)找到User 记录。

但是结果是这样的:

User.where(created_at: '2020-08-07 11:30:28.5934908').first
=> nil

在 Rails 生成的 MySQL 查询中,找不到此(现有)用户记录的原因似乎很明显:

User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`.`created_at` = '2020-08-07 11:30:28.593490' ORDER BY `users`.`id` ASC LIMIT 1

...由于某种原因,最后一个数字 8 从 MySQL 查询中使用的时间戳 2020-08-07 11:30:28.5934908 中删除。

这里有什么问题? Rails 会缩短查询中的时间戳吗?或者 MySQL 会这样做吗?我该如何解决?

【问题讨论】:

  • MySQL 仅支持带有 microsecond precision(小数点后 6 位)的时间戳,但您的查询指定了 7 位(精度为数百纳秒)。您确定查询中的时间戳正确吗? MySQL中created_at列的定义是什么?
  • @BoraMa - 你是对的,MySQL 表显示了datetime(6) 定义。我的查询中使用的第 7 位数字来自像 User.first.created_at.to_f 这样的转换。如果不是.created_at.to_f,将datetime 转换为精确的float(秒数)的正确方法是什么?

标签: mysql ruby-on-rails


【解决方案1】:

根据您的 cmets,我认为主要问题在于将微秒精确时间转换为 Float。浮点数(即使在 ruby​​ 内部是一个 Double)没有足够的精度来以微秒精度完全表示所有日期/时间,documented in the Time class 也是如此(尽管他们谈论的是“纳秒”,有趣的是)。然后,这种转换只会尝试在 Float 中找到最接近的可能表示。将得到的浮点数四舍五入到 6 位可能有效,但我不确定它是否保证始终有效……

假设存储在 DB 中的实时时间是2020-08-07 11:30:28.593491。正如您所注意到的,这会不精确地转换为 Float:

>> Time.parse('2020-08-07 11:30:28.593491').to_f
=> 1596792628.5934908

保证的方法是使用Rational number,即to_r

>> Time.parse('2020-08-07 11:30:28.593491').to_r
=> (1596792628593491/1000000)

要从有理数重建时间,您可以使用Time.at

>> Time.at(Rational(1596792628593491, 1000000)).usec
=> 593491

请注意,此处完全保留了微秒。

因此,精确存储created_at 时间并在以后使用它来搜索记录涉及使用有理数变量而不是浮点数:

>> user_created_at = User.first.created_at.to_r
=> (1596792628593491/1000000)

>> User.where(created_at: Time.at(user_created_at)).first == User.first
=> true

另一种方法可能是将自纪元以来的整数秒 (User.first.created_at.to_i) 和纳秒分数 (User.first.created_at.usec) 分别存储在两个变量中。它们也可以用于Time.at,以重建时间。

作为旁注,Rails issue 也对此进行了讨论,得出了类似的结论。

【讨论】:

    【解决方案2】:

    这不是对您问题的直接回答,而是一种解决方法。您可以像这样在一些非常小的时间增量中查询created_at 列。

    User.where(created_at: '2020-08-07 11:30:28.593490'...'2020-08-07 11:30:28.593491')
    

    【讨论】:

      【解决方案3】:

      根据@BoraMa 的评论(谢谢!),MySQL created_at 时间戳必须只有 6 位数字。因此,工作查询将如下所示:

      User.where(created_at: '2020-08-07 11:30:28.5934908'.round(6)).first
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-09-20
        • 2014-09-19
        • 2011-09-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多