如何使这个长SQL查询成为完整的连接?

时间:2016-05-31 15:03:13

标签: mysql

我有以下sql查询。它用于获取本周只登录过一次的用户的统计信息。我的问题是我遗漏了一些数据。当我运行一个简单的查询来查看本周只有多少用户登录时,我得到五行但这个查询只返回四行。我认为这是因为这些表只是保持连接状态。当我在查询中创建表时,我遇到了麻烦,并在尝试添加union语句以使其成为完全连接时不断出错。这是查询任何帮助赞赏。

SELECT a.user_id, 
   a.logins, 
   a._date, 
   COALESCE(b.loaded, 0)    loaded, 
   COALESCE(c.attempted, 0) attempted, 
   COALESCE(d.correct, 0)   correct 
FROM   (SELECT l.user_id, 
           l.in_datetime, 
           Date_format(l.in_datetime, '%d/%m/%Y') _date, 
           Count(*)                               AS logins 
    FROM   production.login l 
    GROUP  BY user_id) a 
   LEFT JOIN (SELECT user_id, 
                     Count(*) AS loaded 
              FROM   production.score s 
                     JOIN processedquestion pq 
                       ON s.attempt_id = pq.attempt_id 
              GROUP  BY user_id) b 
          ON a.user_id = b.user_id 
   LEFT JOIN (SELECT user_id, 
                     Count(*) AS attempted 
              FROM   production.score s 
                     JOIN processedquestion pq 
                       ON s.attempt_id = pq.attempt_id 
              WHERE  s.selected_answer IS NOT NULL 
              GROUP  BY user_id) c 
          ON c.user_id = b.user_id 
   LEFT JOIN (SELECT user_id, 
                     Count(*) AS correct 
              FROM   production.score s 
                     JOIN processedquestion pq 
                       ON s.attempt_id = pq.attempt_id 
              WHERE  s.selected_answer = s.correct_answer 
              GROUP  BY user_id) d 
          ON c.user_id = d.user_id 
WHERE  logins = 1 
   AND Year(a.in_datetime) = Year(Curdate()) 
   AND Week(a.in_datetime) = Week(Curdate()) 

1 个答案:

答案 0 :(得分:1)

我不认为这个问题与完整联接有关。问题是您需要将登录日期过滤器移动到表表达式中。上面的查询会查找在整个表中只有一个登录 的用户,这就是为什么您的结果较少的原因。

另请注意,您的查询无法在正确禁止在分组查询中返回非聚合列的系统上运行。在你的情况下,你只想要一个日期,所以它并不重要;但是,正确的方法是在min()计算中使用_date之类的虚拟聚合。我之所以这样称呼它是因为它是MySQL开发人员遇到的许多问题的根源。

单一登录条件也可以用having表示,这有利于将逻辑的一部分保存在一个地方,而不需要公开单独的计数列以便稍后引用。我认为这可能是一个偏好问题,尽管我认为使用语言中内置的工具是有意义的。

我还将多个联接合并到一个表中,这样可以让它更容易理解。

select
    ...
from
    (
        select user_id, min(date_format(in_datetime, '%d/%m/%Y')) _date,
        from production.login
        where year(in_datetime) = year(curdate()) and week(in_datetime) = week(curdate())
        group by user_id
        having count(*) = 1
    ) users
        left outer join
    (
        select
            s.user_id, /* I qualified with s but not sure that was the right table */
            count(*) as loaded,
            count(s.selected_answer) as attempted,
            count(case when s.selected_answer = s.corrected_answer then 1 end) as correct
        from production.score s inner join processedquestion pq
            on pq.attempt_id = s.attempt_id
        group by user_id
    ) questions
        on questions.user_id = users.user_id

我不知道您的登录表有多大,但如果您要计算开始日期和结束日期并使用in_datetime between <start_of_week> and <end_of_week>而不是基于提取年份和星期部分的检查,则查询可能会更有效地运行。实际上,当你在1月的第一周使用它时,我认为你会遇到更严重的问题。