【问题标题】:AVG(TIMESTAMPDIFF) mySQL returning wrong answersAVG(TIMESTAMPDIFF) mySQL 返回错误答案
【发布时间】:2014-12-04 20:30:38
【问题描述】:

下面是我用来计算用户平均会话持续时间的代码。

SELECT 
    tbl.create_Date
   ,HourOfDay
   ,HourOfDay_AMPM

   ,AVG(TIMESTAMPDIFF(SECOND, tbl.minDt, tbl.maxDt))/60 AS Duration_mins

  FROM (SELECT 
           i.session_id,
           i.createDate,
           DATE(CONVERT_TZ(i.createDate, '+00:00', '-04:00')) as create_Date,
           HOUR(CONVERT_TZ(i.createDate, '+00:00', '-04:00')) as HourOfDay,
           DATE_FORMAT(CONVERT_TZ(i.createDate,'+00:00','-04:00'), '%l%p') as HourOfDay_AMPM,
           min(i.createDate) minDt,
           max(i.createDate) maxDt,
           (max(i.createDate) - min(i.createDate) )/60 as Duration
      FROM impressions i 

     WHERE i.createDate >= current_date
     AND HOUR(CONVERT_TZ(i.createDate, '+00:00', '-04:00')) >=9
     AND HOUR(CONVERT_TZ(i.createDate, '+00:00', '-04:00')) < 22
     AND i.session_Id <> ''


     GROUP BY i.session_id
     HAVING Duration > 0
     ORDER BY i.createDate, i.session_id

        ) as tbl
 GROUP BY  tbl.create_DATE, tbl.HourOfDay

 ORDER by tbl.create_Date

注意,数据库中的时区是 UTC,我需要以 EST 显示结果,这就是我使用 convert_TZ 命令的原因。

问题:我运行 inner 查询并将原始数据粘贴到 Excel 中,生成数据透视表并得到以下结果

Hour    Avg_duration_mins
9AM     14.43
10AM    59.17
11AM    24.55
12PM    12.69
2PM     1.27

但是,按原样运行整个查询会给我以下结果

 Hour    Avg_duration_mins
 9AM    6.98
10AM    18.78
11AM    9.40
12PM    7.49
 2PM    1.21

经过手动检查,excel结果准确且有意义。为什么 SQL 会发疯?我感觉问题出在AVG 函数和maxmin 的聚合上。

更新:对于表格展示次数,同一session_id 可以有多个条目

session_id     |   createDate     |    actions     |
   023awv        2014-10-09 12:02     some action
   023awv        2014-10-09 12:12     some action
   023awv        2014-10-09 12:22     some action
   023awv        2014-10-09 12:32     some action
   011awv        2014-10-09 12:42     some action
   023awv        2014-10-09 12:42     some action
   023awv        2014-10-09 12:52     some action
   023awv        2014-10-09 12:53     some action
   052brw        2014-10-09 13:02     some action
   023awv        2014-10-09 13:05     some action
   023awv        2014-10-09 13:06     some action
   023awv        2014-10-09 13:08     some action
   023awv        2014-10-09 13:12     some action

我想以每小时/每天为单位获取每个会话的平均持续时间。

任何帮助将不胜感激。

【问题讨论】:

  • 会话时长为 70 分钟。应该计入 12:00 桶还是 13:00 桶?查询的方式本质上是随机选择。
  • 这是一个很好的观点,我也遇到了问题。我认为它会选择会话的开始时间并将其存储在该插槽中。
  • @Laurence:如果你能帮助我重新开始,我将不胜感激。我觉得自己已经够糊涂了。如果你要这样做,你会怎么做?我觉得AVG 函数没有计算我认为它应该计算的值。运行内部查询,我得到了正确的每小时槽数行数,并且我觉得按 HOUR 对其进行分组将使平均值能够在该小时级别上获取。我错了吗?
  • 我已经更新了答案。
  • 按照建议运行查询;我仍然得到同样的错误结果。

标签: mysql sql excel timestamp average


【解决方案1】:

如果您在 Excel 计算中使用 (max(i.createDate) - min(i.createDate) )/60 as Duration 作为分钟数,那么这是错误的。减去日期提供了某种间隔表示:

select timestamp('2014-10-09 14:12') - timestamp('2014-10-09 13:04');

> 10800

这是“1 小时 8 分钟”而不是 4080 秒。

您的内部查询有一个分组依据,但也包括非聚合的非分组列。简单来说:

select
    session_id,
    createDate -- this isn't grouped or aggregated
from
    impressions i
group by
    session_id

大多数数据库不允许您这样做。 MySQL 将返回每个session_id 发生的第一个createDate。所以你的内部查询会产生不稳定的结果。单独运行它的查询计划可能与一起运行查询的查询计划不同。所以它最终会在每种情况下返回不同的值。

假设展示次数表包含以下两行:

session_id | createDate
--------------------------------
         1 | 2014-10-09 13:30:00
         1 | 2014-10-09 15:30:00

内部查询应该返回什么?外部查询应该返回什么?

解决问题的一种方法是根据最短日期对结果进行分组:

select
    tbl.Create_Date,
    tbl.HourOfDay,
    tbl.HourOfDay_AMPM,
    avg(timestampdiff(second, tbl.minDt, tbl.maxDt))/60 as Duration_mins
from (
    select
       i.session_id,
       date(convert_tz(min(i.createDate), '+00:00', '-04:00')) as create_Date,
       hour(convert_tz(min(i.createDate), '+00:00', '-04:00')) as HourOfDay,
       date_format(convert_tz(min(i.createDate), '+00:00', '-04:00'), '%l%p') as HourOfDay_AMPM,
       min(i.createDate) minDt,
       max(i.createDate) maxDt,
       (max(i.createDate) - min(i.createDate) )/60 as Duration
    from
        impressions i 
    where
        i.createDate >= current_date and
        hour(convert_tz(i.createDate, '+00:00', '-04:00')) >=9 and
        hour(convert_tz(i.createDate, '+00:00', '-04:00')) < 22 and
        i.session_Id <> ''
    group by
        i.session_id
    having
        Duration > 0
    ) as tbl
group by
    tbl.Create_Date,
    tbl.HourOfDay,
    tbl.HourOfDay_AMPM
order by
    tbl.create_Date,
    tbl.HourOfDay

在这里,我基本上用 min(i.CreatDate) 替换了内部查询中每个非聚合出现的 i.CreateDate。这使得内部查询定义良好。 IE。它只能返回一个结果集。

通过阅读 MySQL 手册,很难弄清楚在这种情况下在内部查询中放置订单会做什么。手册说外部订单优先于内部订单。

【讨论】:

  • 感谢您的信息,但我很好奇您能否提出解决方案。我是新手,仍然对您的解释感到困惑。如果你要编辑代码,你会怎么做?
  • @eager_learner313 我在回答的最后提出了一个问题,以供讨论。
  • 内部查询每次返回相同的结果。它是返回不同结果的外部。内部查询应产生以下 session_id | minDt | maxDt 1 | 2014-10-09 13:30:00 | 2014-10-09 15:30:00
  • @eager_learner313 内部查询多于三列,例如i.createDate应该返回什么?
  • 让我提供一个示例表的示例。这会有帮助吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-08-20
  • 1970-01-01
  • 1970-01-01
  • 2018-10-03
  • 1970-01-01
  • 2022-08-17
  • 1970-01-01
相关资源
最近更新 更多