MYSQL - 按周分组,同时显示没有值的周数

时间:2016-04-09 18:16:55

标签: mysql sql

我正在尝试完成以下操作 - 我希望在每个客户的网站上看到按日历周分组的会话。以下是我到目前为止要完成的查询:

SELECT o.name
     , s.organization_id
     , count(s.id) as num_of_sessions
     , CONCAT(s.created_at, ' - ', s.created_at + INTERVAL 6 DAY) AS week
  FROM triton.sessions s
     , triton.organizations o
 where o.id=s.organization_id
   and s.organization_id in (17,19,20,21,24,25,26,27,29,31,32,33,34,25,36,37,38,39,40,41,43,44,45,46,47,48,49,50,51,52,53,56,57,58,59,60,62,69,70,72)
 GROUP BY s.organization_id, WEEK(s.created_at)
 ORDER BY o.name, WEEK(s.created_at);

问题在于,客户没有网站会话的周数不报告为0 - 而是报告该周没有报告。这是一个问题,因为我无法轻松地将数据转换为Excel,并为每个客户的会话创建图表。

为了尝试解决此问题,我创建了一个临时周表,其中每周的值为1-52,并尝试使用此链接中建议的方法:Summarise by week, even for empty rows

面临的挑战是,当我进行左外连接时,我会为组织丢失组。

这是一个工作的SQL,用于按周分组(在尝试按组织分组之前):

select w.weeknum
     , sess.club
     , sess.organization_id
     , count(sess.club) from weeks w 
  left outer
  join ( select o.name as club
              , s.organization_id
              , s.created_at
           from sessions s
              , organizations o
          where s.organization_id in (17,19,20,21,24,25,26,27,29,31,32,33,34,25,36,37,38,39,40,41,43,44,45,46,47,48,49,50,51,52,53,56,57,58,59,60,62,69,70,72)
            and o.id=s.organization_id
       ) sess
    on (w.weeknum = extract(week from sess.created_at))
 group by w.weeknum

上面的代码只返回52行(每周1行),计数给我每周会话数。

我现在想扩展上面的代码来完成上述操作,但每个组织。我应该回到52 * N行,其中N是组织的数量。我认为这就像将组织添加到groupby语句一样简单,但它只返回了有sesssions的周(导致我从一开始就遇到的问题)。这是查询:

select w.weeknum
     , sess.club
     , sess.organization_id
     , count(sess.club)
  from weeks w
  left outer
  join ( select o.name as club
              , s.organization_id
              , s.created_at
           from sessions s
              , organizations o
          where s.organization_id in (17,19,20,21,24,25,26,27,29,31,32,33,34,25,36,37,38,39,40,41,43,44,45,46,47,48,49,50,51,52,53,56,57,58,59,60,62,69,70,72)
           and o.id=s.organization_id
       ) sess
    on (w.weeknum = extract(week from sess.created_at))
 group by sess.club, w.weeknum
 order by sess.club

有没有人有任何其他建议可以实现我的目标?基本上对于我的每个客户,我希望按周查看会话列表(即使他们在特定的一周内没有会话)。

2 个答案:

答案 0 :(得分:0)

使用cross join获取行,然后使用left join

select w.weeknum, s.club, s.organization_id, count(s.club)
from weeks w cross join
     organizations o left outer join
     sessions s
     on w.weeknum = extract(week from s.created_at) and
        o.id = s.organization_id
where o.id in (17,19,20,21,24,25,26,27,29,31,32,33,34,25,36,37,38,39,40,41,43,44,45,46,47,48,49,50,51,52,53,56,57,58,59,60,62,69,70,72)
group by w.weeknum, s.club, s.organization_id;

答案 1 :(得分:0)

只需获取原始查询,并将其包装在内部并将其用作内联视图。

看起来你想知道你想要一个返回你想要返回的所有行的行源,然后是一个左连接到你的内联视图。

你有这个部分:

 from weeks w

只需对要返回的所有organization_id进行交叉连接。

看起来organization_id可能是组织表的主键。如果是这种情况,那么此查询将返回您想要的集合:

SELECT v.name
     , v.organization_id 
  FROM triton.organizations v
 WHERE v.organization_id (17,19,20,21,24,25,26,27,29,31,32,33,34,25,36,37,38,39,40,41,43,44,45,46,47,48,49,50,51,52,53,56,57,58,59,60,62,69,70,72)
 ORDER BY v.organization_id

所以只需将该集与weeks rowsource:

进行交叉连接即可
  SELECT v.name
       , v.organization_id
       , w.weeknum
    FROM triton.organizations v
   CROSS
    JOIN weeks w
   WHERE v.organization_id (17,19,20,21,24,25,26,27,29,31,32,33,34,25,36,37,38,39,40,41,43,44,45,46,47,48,49,50,51,52,53,56,57,58,59,60,62,69,70,72)
   ORDER
      BY v.organization_id
       , w.weeknum

如果您只想要weeks表中的子集,只需在WHERE子句中添加谓词,例如

 AND w.weeknum BETWEEN 0 AND 104

对于每个organization_id,这应该每周返回一次。

一旦你开始工作,现在只需在你的原始查询中添加一个“外连接”,在SELECT列表中添加一个表达式,它可以获得一个与weeknum匹配的值。

我对涉及created_at的表达感到困惑。由于created_atGROUP BY返回的值不确定。如果您想要“最早”和/或“最新”值,请使用MIN和MAX聚合。 (假设created_at是DATE,DATETIME或TIMESTAMP。)

  SELECT v.name
       , v.organization_id
       , w.weeknum
       , IFNULL(t.num_of_sessions,0)
    FROM triton.organizations v
   CROSS
    JOIN weeks w
    LEFT
    JOIN (
           -- query to get session counts goes here 
         ) t
      ON t.organization_id = v.organization_id
     AND t.weeknum         =  w.weeknum
   WHERE v.organization_id (17,19,20,21,24,25,26,27,29,31,32,33,34,25,36,37,38,39,40,41,43,44,45,46,47,48,49,50,51,52,53,56,57,58,59,60,62,69,70,72)
   ORDER
      BY v.organization_id
       , w.weeknum

在外部查询中,引用视图返回的num_of_sessions列。 IFNULL功能是用零替换“缺失”计数的便捷方式。

获取“计数”的查询可能类似于:

         SELECT s.organization_id    AS organization_id 
              , WEEK(s.created_at)   AS weeknum
              , COUNT(s.id)          AS num_of_sessions
              , MIN(s.created_at)    AS min_created_at
              , MAX(s.created_at)    AS max_created_at
           FROM triton.sessions s
          WHERE s.organization_id IN (17,19,20,21,24,25,26,27,29,31,32,33,34,25,36,37,38,39,40,41,43,44,45,46,47,48,49,50,51,52,53,56,57,58,59,60,62,69,70,72)
          GROUP
             BY s.organization_id
              , WEEK(s.created_at)

如果原始查询的这一部分的意图:

  CONCAT(s.created_at, ' - ', s.created_at + INTERVAL 6 DAY) AS week

是显示一周的开始日期和结束日期,然后从周表中的值生成。 (如果有一个组织,在一个星期内只有一个交易,并且它在星期四,那个表达将产生“星期四到星期三”。这样做没有错,但我强烈怀疑这不是什么你真的想要。

如果你想要每周的“星期六星期六”,那么最好从周末表中返回。

如果您想要组织会话的实际日期,请使用created_at的MIN()和MAX()值,并将它们连接起来。这些不一定是“星期六到星期六”,但任何返回的日期都将在“一周之内”。