python - 为什么 MYSQL DB 在对 Django models.DateTimeField 进行平均时返回损坏的值?

标签 python mysql django django-models mariadb

我在 MySQL(实际上是 MariaDB)数据库上运行 Django 应用程序。

我的 Django 模型如下所示:

from django.db import models
from django.db.models import Avg, Max, Min, Count

class myModel(models.Model):
    my_string = models.CharField(max_length=32,)
    my_date = models.DateTimeField()

    @staticmethod
    def get_stats():            
        logger.info(myModel.objects.values('my_string').annotate(
                count=Count("my_string"), 
                min=Min('my_date'), 
                max=Max('my_date'), 
                avg=Avg('my_date'),
            )
        )

当我运行 get_stats() 时,我得到以下日志行:

[2015-06-21 09:45:40] INFO [all_logs:96] [{'my_string': u'A', 'count': 2, 'avg': 20080507582679.5, 'min': datetime.datetime(2007, 8, 2, 11, 33, 53, tzinfo=<UTC>), 'max': datetime.datetime(2009, 2, 13, 5, 20, 6, tzinfo=<UTC>)}]

我遇到的问题是数据库返回的 my_date 字段的平均值是:20080507582679.5。仔细看那个数字。这是无效的日期格式。

为什么数据库不返回这两个日期的平均值的有效值?如果所描述的方式失败,我如何获得该字段的实际平均值? Django DateTimeField 是否未设置为处理平均?

最佳答案

Q1:为什么数据库没有返回这两个日期的平均值的有效值?

答:返回的值是预期的,它是明确定义的 MySQL 行为。

MySQL automatically converts a date or time value to a number if the value is used in a numeric context and vice versa.

MySQL 引用手册:https://dev.mysql.com/doc/refman/5.5/en/date-and-time-types.html


在 MySQL 中,AVG 聚合函数对数字 值进行操作。

在 MySQL 中,DATEDATETIME 表达式可以用数字 上下文。

作为一个简单的演示,对DATETIME 执行数字 加法运算会将日期时间值隐式转换为数字。这个查询:

  SELECT NOW(), NOW()+0

返回如下结果:

  NOW()                                NOW()+0  
  -------------------  -----------------------
  2015-06-23 17:57:48    20150623175748.000000

请注意,表达式 NOW()+0 的返回值不是 DATETIME,它是一个数字

当您在 DATETIME 表达式上指定 SUM()AVG() 函数时,这等同于转换 DATETIME 转化为一个数,然后求和 或平均数。

也就是说,此表达式 AVG(mydatetimecol) 的返回值等同于此表达式的返回值:AVG(mydatetimecol+0)

被“平均”的是一个数值。您已经观察到,返回的值不是有效的日期时间;即使在它恰好看起来像有效日期时间的情况下,它也可能不是您认为真正的“平均值”的值。


Q2:如果上述方法失败,如何获取该字段的实际平均值?

A2:一种方法是将日期时间转换为可以“准确”求平均值的数值,然后再将其转换回日期时间。

例如,您可以将日期时间转换为表示某个固定时间点的秒数的数值,例如

  TIMESTAMPDIFF(SECOND,'2015-01-01',t.my_date)

然后您可以“平均”这些值,以获得固定时间点的平均秒数。 (注意:注意加起来的行数非常多,值非常大,超过限制(最大数值),数值溢出问题。)

  AVG(TIMESTAMPDIFF(SECOND,'2015-01-01',t.my_date))

要将其转换回日期时间,将该值作为秒数添加回固定时间点:

  '2015-01-01' + INTERVAL AVG(TIMESTAMPDIFF(SECOND,'2015-01-01',t.my_date)) SECOND

(请注意,DATEIME 值是在 MySQL session 的时区中计算的;因此在某些边缘情况下,MySQL session 中的 time_zone 变量的设置将对返回值有一定影响。)

MySQL 还提供了一个 UNIX_TIMESTAMP() 函数,它返回一个 unix 风格的整数值,即从纪元开始(UTC 时间 1970 年 1 月 1 日午夜)开始的秒数。您可以使用它来更简洁地完成相同的操作:

  FROM_UNIXTIME(AVG(UNIX_TIMESTAMP(t.my_date)))

请注意,这个最终表达式实际上在做同样的事情...将日期时间值转换为自“1970-01-01 00:00:00”UTC 以来的秒数,取其平均值,然后将该平均秒数添加回“1970-01-01”UTC,最后将其转换回 DATETIME 值,以当前 session time_zone 表示。


问题 3:Django DateTimeField 是否未设置为处理平均?

答: 显然,Django 的作者对 SQL 表达式 AVG(datetime) 从数据库返回的值感到满意。

关于python - 为什么 MYSQL DB 在对 Django models.DateTimeField 进行平均时返回损坏的值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30963319/

相关文章:

sql - mysql查询复杂分组

php - 有效地根据时间(小时)拆分数据

python - 其他列的南迁移默认值?

python - 检查2个列表是否具有相同的值或值、索引

python - numpy 将向量转换为二进制矩阵

python - Numpy 高效构造稀疏 coo_matrix 或更快的列表扩展

python - Geopy:计算 GPS 航向/方位

php - 产品表连接库存

Django、Tastypie 和检索新对象数据

django - 将 django static 部署到 amazon s3