为什么 MYSQL DB return 在对 Django models.DateTimeField 取平均值时出现损坏的值?
Why does MYSQL DB return a corrupted value when averaging over a Django models.DateTimeField?
我运行在 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>)}]
我遇到的问题是,数据库 return 编辑的 my_date 字段的平均值是:20080507582679.5
。仔细看那个数字。这是无效的日期格式。
为什么数据库 return 不是这两个日期的平均值的有效值?如果所描述的方式失败,我如何获得该字段的实际平均值? Django DateTimeField 是否未设置为处理平均?
当你使用values()
时,Django 不会转换它从数据库-python连接器得到的值。由连接器决定返回值的方式。
在这种情况下,MySQL 连接器 returns 似乎是一个删除了分隔符的字符串表示形式。您可以尝试使用 datetime.strptime()
和匹配的 format
将其解析为 datetime
对象。
方案 A:使用 TIMESTAMP 字段而不是 DATETIME 字段
方案 B:在计算过程中将 DATETIME 转换为 TIMESTAMP:
FROM_UNIXTIME(ROUND(AVG(UNIX_TIMESTAMP(`my_date`))))
(抱歉,我不知道所需的 Django 语法。)
问题 1:为什么数据库 return 不是这两个日期的平均值的有效值?
A: 值 returned 是预期的,它是明确定义的 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
聚合函数对 numeric 值进行运算。
在MySQL中,DATE
或DATETIME
表达式可以在数字 上下文。
作为一个简单的演示,对 DATETIME
执行 numeric 加法运算会将日期时间值隐式转换为数字。此查询:
SELECT NOW(), NOW()+0
return结果如下:
NOW() NOW()+0
------------------- -----------------------
2015-06-23 17:57:48 20150623175748.000000
请注意,表达式 NOW()+0
的值 return 是 而不是 a DATETIME
, 这是一个数字.
在DATETIME
表达式上指定SUM()
或AVG()
函数时,相当于将DATETIME
转换为数字,然后求和
或平均数。
即,此表达式 AVG(mydatetimecol)
中的 return 等同于此表达式中的 return:AVG(mydatetimecol+0)
"averaged" 是一个数值。您已经观察到,值 returned 不是有效的日期时间;甚至在它恰好看起来像有效日期时间的情况下,它也可能不是您认为的真实值 "average".
Q2:如果上述方法失败,如何获取该字段的实际平均值?
A2: 一种方法是将日期时间转换为可以 "accurately" 取平均值的数值,然后将其转换回日期时间。
例如,您可以将日期时间转换为表示某个固定时间点的秒数的数值,例如
TIMESTAMPDIFF(SECOND,'2015-01-01',t.my_date)
然后您可以 "average" 这些值,以获得从固定时间点开始的平均 秒数 。 (注意:注意加起来的行数特别多,数值特别大,超过限制(最大数值),数值溢出问题。)
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 会话的时区中计算的;因此在某些情况下 time_zone
变量的设置在 MySQL session 会对 returned 的值产生一些影响。)
MySQL 还提供了一个 UNIX_TIMESTAMP()
函数,它 return 是一个 unix 风格的整数值,从时代开始的秒数(1970 年 1 月 1 日午夜 UTC) .您可以使用它来更简洁地完成相同的操作:
FROM_UNIXTIME(AVG(UNIX_TIMESTAMP(t.my_date)))
请注意,这个最终表达式实际上在做同样的事情...将日期时间值转换为自“1970-01-01 00:00:00”UTC 以来的秒数,取其平均值,然后将该平均秒数添加回“1970-01-01”UTC,最后将其转换回 DATETIME
值,在当前会话 time_zone
.
中表示
问题 3:Django DateTimeField 是否没有设置处理平均?
A: 显然,Django 的作者对从数据库中为 SQL 表达式 return 编辑的值 return 感到满意 AVG(datetime)
.
我运行在 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>)}]
我遇到的问题是,数据库 return 编辑的 my_date 字段的平均值是:20080507582679.5
。仔细看那个数字。这是无效的日期格式。
为什么数据库 return 不是这两个日期的平均值的有效值?如果所描述的方式失败,我如何获得该字段的实际平均值? Django DateTimeField 是否未设置为处理平均?
当你使用values()
时,Django 不会转换它从数据库-python连接器得到的值。由连接器决定返回值的方式。
在这种情况下,MySQL 连接器 returns 似乎是一个删除了分隔符的字符串表示形式。您可以尝试使用 datetime.strptime()
和匹配的 format
将其解析为 datetime
对象。
方案 A:使用 TIMESTAMP 字段而不是 DATETIME 字段
方案 B:在计算过程中将 DATETIME 转换为 TIMESTAMP:
FROM_UNIXTIME(ROUND(AVG(UNIX_TIMESTAMP(`my_date`))))
(抱歉,我不知道所需的 Django 语法。)
问题 1:为什么数据库 return 不是这两个日期的平均值的有效值?
A: 值 returned 是预期的,它是明确定义的 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
聚合函数对 numeric 值进行运算。
在MySQL中,DATE
或DATETIME
表达式可以在数字 上下文。
作为一个简单的演示,对 DATETIME
执行 numeric 加法运算会将日期时间值隐式转换为数字。此查询:
SELECT NOW(), NOW()+0
return结果如下:
NOW() NOW()+0
------------------- -----------------------
2015-06-23 17:57:48 20150623175748.000000
请注意,表达式 NOW()+0
的值 return 是 而不是 a DATETIME
, 这是一个数字.
在DATETIME
表达式上指定SUM()
或AVG()
函数时,相当于将DATETIME
转换为数字,然后求和
或平均数。
即,此表达式 AVG(mydatetimecol)
中的 return 等同于此表达式中的 return:AVG(mydatetimecol+0)
"averaged" 是一个数值。您已经观察到,值 returned 不是有效的日期时间;甚至在它恰好看起来像有效日期时间的情况下,它也可能不是您认为的真实值 "average".
Q2:如果上述方法失败,如何获取该字段的实际平均值?
A2: 一种方法是将日期时间转换为可以 "accurately" 取平均值的数值,然后将其转换回日期时间。
例如,您可以将日期时间转换为表示某个固定时间点的秒数的数值,例如
TIMESTAMPDIFF(SECOND,'2015-01-01',t.my_date)
然后您可以 "average" 这些值,以获得从固定时间点开始的平均 秒数 。 (注意:注意加起来的行数特别多,数值特别大,超过限制(最大数值),数值溢出问题。)
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 会话的时区中计算的;因此在某些情况下 time_zone
变量的设置在 MySQL session 会对 returned 的值产生一些影响。)
MySQL 还提供了一个 UNIX_TIMESTAMP()
函数,它 return 是一个 unix 风格的整数值,从时代开始的秒数(1970 年 1 月 1 日午夜 UTC) .您可以使用它来更简洁地完成相同的操作:
FROM_UNIXTIME(AVG(UNIX_TIMESTAMP(t.my_date)))
请注意,这个最终表达式实际上在做同样的事情...将日期时间值转换为自“1970-01-01 00:00:00”UTC 以来的秒数,取其平均值,然后将该平均秒数添加回“1970-01-01”UTC,最后将其转换回 DATETIME
值,在当前会话 time_zone
.
问题 3:Django DateTimeField 是否没有设置处理平均?
A: 显然,Django 的作者对从数据库中为 SQL 表达式 return 编辑的值 return 感到满意 AVG(datetime)
.