SQL连接3个表与最新组值分组

时间:2014-12-06 05:18:15

标签: mysql date join

我有3张桌子。具有以下结构的用户,消息和user_analytics:

  • user(userId) - 包含所有用户
  • message(messageId(pk),userId(fk),time) - 包含所有消息
  • user_analytics(user_analyticsId(pk),userId(fk),设备,时间) -
    包含在连接上收集的数据
user : messages (1:n)
user : device (1:n)

现在我想知道每天用什么设备发送了多少条消息。因此,我首先需要为每条消息收集用于发送消息的设备(桌面,iOS,Android),具体取决于消息时间本身。这意味着我需要user_analytics.time< = message.time并显示最新结果。

我看到很多关于每组最大的解决方案,但我没有让它发挥作用。

我只能使用需要20秒的子查询(user_analytics保存100k记录和消息3k ......所以不多):

select  date_format(m.time,'%Y-%m-%d') as date,
        count(*) as message_count,
        ua.device
from    message m,
        user u left join user_analytics ua on (
            u.userId = ua.userId and
            ua.user_analyticsId = ( select max(user_analyticsId) 
                                from    user_analytics
                                where   userId = m.userId and
                                        time < m.time))
where   m.userId = u.userId
group by 1,3;

但这看起来非常低效。还有其他方法可以达到同样的效果吗?

更新 我忘了提到我在用户表上有一个重要的条件。这就是我需要加入此表的原因。

我创建了一个sql小提琴,给你一个例子。目前我已经实施了Jaguar Chang's解决方案,比我的快100倍:

sql fiddle

3 个答案:

答案 0 :(得分:2)

没有必要加入用户表,因此您可以像这样简化代码:

select  date_format(m.time,'%Y-%m-%d') as date,
        count(*) as message_count,
        ua.device
from    message m,
        left join user_analytics ua on (
            m.userId = ua.userId and
            ua.user_analyticsId = ( select max(user_analyticsId) 
                                from    user_analytics
                                where   userId = m.userId and
                                        time < m.time))
group by 1,3;

这可能效率不高,但你可以试试这个:

select  date_format(t2.time,'%Y-%m-%d') as date,
        count(*) as message_count,
        t2.last_device
from    
    (select 
      @device := 
          if(@uid = userid,
             if(tbl = 'm' ,@device, device),
             if(@uid := userid,device,device)) as last_device
      ,t1.*
      from 
          (select @device := '' , @uid :=0) as t0
      join
          (select 'ua' as tbl,userid,time,device from user_analytics
           union all
           select 'm' as tbl,userid,time,null as device from messages
          ) as t1
      order by userid,time
    ) as t2
where tbl='m'
group by 1,3;

我猜你的最初目的是按照设备上的连接时间来划分消息,因此按时间序列对消息和连接记录进行排序,然后您可以获得每个消息的最后一次连接所使用的设备。

我认为这种方法效率很高,因为100k + 3k排序将比3k * 100k * 100k连接操作快得多

测试Sql Fiddle Demo

答案 1 :(得分:1)

那么这个比较如何(随着数据集的增长可能不那么有利......)

SELECT DATE(message_time) dt
     , b.device
     , COUNT(*)
  FROM 
     (
       SELECT m.id message_id
            , m.userid
            , m.time message_time
            , MAX(um.time) device_time
         FROM messages m
         JOIN user_analytics um
           ON um.userid = m.userid
          AND um.time <= m.time
        GROUP 
           BY m.id
      ) a
  JOIN user_analytics b
    ON b.userid = a.userid
   AND b.time = a.device_time
 GROUP
    BY DATE(message_time)
     , b.device;

答案 2 :(得分:0)

不太确定你想要什么,但你说你的查询正在运行你只是想要一种有效的方式。所以,试试这个,让我知道它是否有效

select  date_format(m.time,'%Y-%m-%d') as date,
        count(*) as message_count,
        ua.device, max(ua.user_analyticsId)
from    message m
        INNER JOIN
        user u ON
        m.userId = u.userId
        LEFT JOIN 
        user_analytics ua ON
        u.userId = ua.userId and       
where  
ua.time<m.time
group by 1,3;

如果您希望将整个查询放入另一个查询的块中,则可以忽略添加的新列

select date, message_count, device from (
    select  date_format(m.time,'%Y-%m-%d') as date,
            count(*) as message_count,
            ua.device, max(ua.user_analyticsId)
    from    message m
            INNER JOIN
            user u ON
            m.userId = u.userId
            LEFT JOIN 
            user_analytics ua ON
            u.userId = ua.userId and       
    where  
    ua.time<m.time
    group by 1,3) A ;