MySQL:与案例

时间:2016-05-03 10:24:04

标签: mysql indexing group-by query-optimization

我正在使用MySQL

我无法改变数据库结构,所以这不是一个可悲的选择

问题:

Scenerio:详细

msgs ,包含已发送邮件的记录,包含字段:

  • ID

  • user_id,(发送消息的人)

  • 类型,(0 =>表示它的组msg。在此下发送的所有消息都由group_id标记。所以假设group_id = 5发送5个消息,该表将有5个记录,group_id = 5和type = 0。对于类型> 0,group_id将为NULL,因为所有其他类型都没有group_id,因为它们是发送给单个收件人的单个消息)

  • group_id(如果type = 0,将包含group_id,否则为NULL)

表包含大约1000万条用户ID 50001和不同类型的记录(即组和个别消息)

现在QUERY:

SELECT 
  msgs.*
FROM
  msgs 
  INNER JOIN accounts 
    ON (
      msgs.user_id = accounts.id
    ) 
WHERE 1 
  AND msgs.user_id IN (50111) 
  AND msgs.type IN (0, 1, 5, 7) 
GROUP BY CASE `msgs`.`type` WHEN 0 THEN `msgs`.`group_id` ELSE `msgs`.`id` END 
ORDER BY `msgs`.`group_id` DESC
LIMIT 100

在一个QUERY中获取摘要

所以发送到群组的消息让我们说5(此表中有5条记录)将显示为摘要的1条记录(我可能会稍后显示COUNT,但这不是问题)。

各个msgs的NULL为group_id,所以我不能把'GROUP BY group_id'coz用于将所有单个msgs分组为单个记录,这是不可接受的。

示例输出可以是:

id    owner_id,    type    group_id    COUNT   
1       50001       0       2           5    
1       50001       1       NULL        1    
1       50001       4       NULL        1    
1       50001       0       7           5
1       50001       5       NULL        1
1       50001       5       NULL        1
1       50001       5       NULL        1
1       50001       0       10          5

现在问题是使用CASE之后的GROUP条件(我目前认为我必须因为我只需要按group_id进行分组,如果type = 0)导致很多延迟因为它没有使用它所做的索引我不使用CASE(就像group_id一样)。请查看上面的SQLFiddles以查看解释结果

任何人都可以提出如何优化的建议

更新

我尝试了一种解决方法,确实可行(将INITIAL查询丢弃到1秒)。使用union,它的作用是最小化结果集,强制SQL在磁盘上为filesort写入(由于结果集很大),限制组msgs的结果集,以及单个msgs(查看下面的查询)

- union的第一部分检索组msgs(类型为0,需要按group_id分组)。应用限制以吸引失控结果集

- 第二个查询检索单个消息,(类型为!= 0的消息,按msgs.id分组 - 不是必需的,只是为了从连接中重复输入保存)。应用限制以吸引失控结果集

- 连接两者以检索所需的结果集

以下是查询:

SELECT 
  * 
FROM
  (
    (
      SELECT 
      msgs.id as reference_id, user_id, type, group_id
    FROM
      msgs 
      INNER JOIN accounts 
        ON (msgs.user_id = accounts.id) 
     WHERE 1 
       AND accounts.id IN (50111 ) AND type = 0
      GROUP BY msgs.group_id 
      ORDER BY msgs.id  DESC
      LIMIT 40
     ) 
    UNION
    ALL 
    (
      SELECT 
      msgs.id as reference_id, user_id, type, group_id
      FROM
      msgs 
      INNER JOIN accounts 
        ON (
          msgs.user_id = accounts.id
        ) 
    WHERE 1 
      AND msgs.type != 0
      AND accounts.id IN (50111) 
    GROUP BY msgs.id 
    ORDER BY msgs.id 
    LIMIT 40
    )
  ) AS temp 
ORDER BY reference_id 
LIMIT 20,20

但是有很多警告,

- 我也需要处理内部查询的限制。让我们说每页20rec,并且我在第4页。对于内部查询,我需要应用限制0,80,因为我不确定这两个部分中的哪一个在前3页中有多少记录。因此,随着每页记录和页数的增长,我的查询变得越来越重。让我们说每页1k rec,而在第100页或者1K,负载越来越重,时间呈指数增长

  • 我需要处理内部查询中的排序,然后应用于union准备的结果集,条件需要单独应用于两个内部查询(但不是很大的问题)

-Cant使用calc_found_rows,因此需要单独使用查询进行计数

主要问题是第一个问题。分页越高,越重,

1 个答案:

答案 0 :(得分:0)

这会跑得更快吗?

SELECT  id, user_id, type, group_id
    FROM  
      ( SELECT  id, user_id, type, group_id, IFNULL(group_id, id) AS foo
            FROM  msgs
            WHERE  user_id IN (50111)
              AND  type IN (0, 1, 5, 7) 
      )
    GROUP BY  foo
    ORDER BY  `group_id` DESC
    LIMIT  100 

需要INDEX(user_id, type)

这是否能够正确地提供正确的'回答?

SELECT  DISTINCT *
    FROM  msgs
    WHERE  user_id IN (50111)
      AND  type IN (0, 1, 5, 7)
    GROUP BY  IFNULL(group_id, id)
    ORDER BY  `group_id` DESC
    LIMIT  100

(它需要相同的索引)