多个LEFT JOINs to self,带有生成分布的标准

时间:2012-08-15 11:49:07

标签: mysql sql left-join self-join

虽然severalquestionscomeclose。到我想要的东西(当我写这个stackoverflow已经提出了更多,但没有一个能解决我的问题),我似乎无法找到我的方法从SQL丛林。

我有一个表(让我们称之为user_classification_fct),它有三个字段:user,week和class(例如,#1周的用户#1有一个'常规用户'类,而用户#2在一周内#1有一类'不常用户')。 (顺便说一句,我已经将实现了类作为INT,但是当我整理SQL时,我希望以VARCHAR的形式使用一些易读的东西。)

我想要做的是根据以下内容生成聚合中用户行为如何变化的摘要报告:

  1. 在第1周和第2周都有50位普通用户和...
  2. 第1周有10位普通用户,但在第2周却不经常使用
  3. 有5位用户从第1周的不常用到第2周的常规用户
  4. ......依旧......
  5. 这使得稍微有点棘手的是用户#5000可能只在第2周开始使用该服务,因此在第1周的表中没有记录。在这种情况下,我希望看到一周的NULL 1和第2周的'常规用户'(或任何合适的用户)。表的大小并不严格相关,但有5周的数据我正在看4200万行,所以我做 想要为仅在第5周开始使用该服务的人插入4个'假'行'非用户'。

    对我来说,这似乎很明显就像在MySQL中使用LEFT或RIGHT JOIN一样,因为NULL应该在'missing'记录中出现。

    我尝试在LEFT JOIN上同时使用WHERE和AND条件,并且我没有得到'正确'的答案(即,在尾随WHERE条件的情况下,我根本没有得到任何NULL值,或者我的计数很远,在下面使用的AND约束的情况下,对于不同用户的数量(大约1000万)来说太高了。这是我最后一次努力实现这个目标:

    SELECT
        ucf1.class_nm AS 'Class in 2012/15',
        ucf2.class_nm AS 'Class in 2012/16',
        ucf3.class_nm AS 'Class in 2012/17',
        ucf4.class_nm AS 'Class in 2012/18',
        ucf5.class_nm AS 'Class in 2012/19',
        count(*) AS 'Count'
    FROM
        user_classification_fct ucf5
    LEFT JOIN user_classification_fct ucf4 
        ON ucf5.user_id=ucf4.user_id 
            AND ucf5.week_key=201219 AND ucf4.week_key=201218
    LEFT JOIN user_classification_fct ucf3 
        ON ucf4.user_id=ucf3.user_id 
           AND ucf4.week_key=201218 AND ucf3.week_key=201217
    LEFT JOIN user_classification_fct ucf2 
        ON ucf3.user_id=ucf2.user_id 
           AND ucf3.week_key=201217 AND ucf2.week_key=201216
    LEFT JOIN user_classification_fct ucf1 
        ON ucf2.user_id=ucf1.user_id 
           AND ucf2.week_key=201216 AND ucf1.week_key=201215
    GROUP BY 1,2,3,4,5;
    

    在查看stackoverflow.com上的各种其他问题时,我可能需要一次一个地执行查询,而UNION将结果集合在一起,或者使用括号将它们一个接一个地链接起来,但那些方法不是我熟悉的(还),我甚至不能得到一个LEFT JOIN(即第5周到第1周,放弃所有其他几周的数据)来返回有用的东西。

    任何提示都会非常受欢迎,我非常感谢在MySQL中工作的建议,因为切换数据库产品不是一种选择。

1 个答案:

答案 0 :(得分:0)

您可以通过分组执行此操作。我将首先总结五周的所有可能组合:

select c_201215, c_201216, c_201217, c_201218, c_201219,
       count(*) as cnt
from (select user_id,
             max(case when week_key=201215 then class_nm end) as c_201215,
             max(case when week_key=201216 then class_nm end) as c_201216,
             max(case when week_key=201217 then class_nm end) as c_201217,
             max(case when week_key=201218 then class_nm end) as c_201218,
             max(case when week_key=201219 then class_nm end) as c_201219
      from user_classification_fct  ucf
      group by user_id
     ) t
group by c_201215, c_201216, c_201217, c_201218, c_201219

这可以解决您的问题。如果你有5个类(包括NULL),那么这将返回最多5 ^ 5或3,125行。

这适用于Excel,因此您可以在那里进行最终处理。或者,您仍然可以使用数据库。

如果您想提取几周,那么我建议将上述内容放入临时表中,比如“t”。并与工会一起做一系列摘录:

select *
from ((select '201215' as weekstart, c_201215, c_201216, sum(cnt) as cnt
       from t
       group by c_201215, c_201216
      ) union all
      (select '201216', c_201216, c_201217, sum(cnt) as cnt
       from t
       group by c_201216, c_201217

      ) union all
      (select '201217', c_201217, c_201218, sum(cnt) as cnt
       from t
       group by c_201217, c_201218

      ) union all
      (select '201218', c_201218, c_201219, sum(cnt) as cnt
       from t
       group by c_201218, c_201219
      )
     ) tg
order by 1, cnt desc

我建议将它放在子查询中,因为您不希望在这么大的表上使用common-subquery优化进行消息传递。您将通过首先总结,然后将数据汇总在一起来得到最终答案。