【问题标题】:How to optimize Mysql Select Query with Union?如何使用 Union 优化 Mysql Select Query?
【发布时间】:2015-10-04 07:33:00
【问题描述】:

我正在使用 InnoDb 存储引擎开发 mysql 数据库。 我的表结构如下:

表名:archiveincomingsms

索引详情:archiveincomingsms

表名称:archiveoutgoingsms

索引详细信息:archiveoutgoingsms

以上是我的表结构及其索引详细信息!

两个表分别至少有超过 10 亿条记录(行)。

现在的问题是,当我想执行以下 SqlQuery 时:

      ( SELECT  id AS ID, `recieved_datetime` `Date`, 'MT' AS Type, src_adress AS Msisdn,
               TEXT as text, CHAR_LENGTH(TEXT) AS QtyOfSymbols, 'OK' AS `Status`
            FROM  archiveincomingsms
            WHERE  1=1
              AND  recieved_datetime BETWEEN '2015-06-14 00:00:00'
                                         AND '2015-07-14 23:59:59'
      )
    UNION  ALL
      ( SELECT  id AS ID, `send_date` `Date`, 'MO' AS Type, scr_adress AS Msisdn,
                TEXT as text, CHAR_LENGTH(TEXT) AS QtyOfSymbols, 'OK' AS `Status`
            FROM  archiveoutgoingsms
            WHERE  1=1
              AND  send_date BETWEEN '2015-06-14 00:00:00'
                                 AND '2015-07-14 23:59:59'
      )
    ORDER BY  `Date` ASC
    LIMIT  0 ,100 

以上查询需要超过 30 秒的时间才能从表中获取数据。 另一个是我也必须为了分页目的计算行数,而且它也需要超过 30 秒的时间。

总体而言,执行时间超过 1 分钟。 有什么合适的方法来优化时间吗? 我必须减少到最多 5 秒。 !怎么可能? 我正在使用mysql数据库!

【问题讨论】:

    标签: php mysql sql database optimization


    【解决方案1】:

    这是查询说明的结果!

    图片可以在新标签中打开查看!这里太小了!

    【讨论】:

      【解决方案2】:

      我看不到索引。

      请,查询结果:

      EXPLAIN (SELECT id AS ID,`recieved_datetime` `Date`,'MT' AS Type,src_adress AS Msisdn,TEXT as text,CHAR_LENGTH(TEXT) AS QtyOfSymbols,'OK' AS `Status` FROM archiveincomingsms
                        WHERE 1=1 AND recieved_datetime BETWEEN '2015-06-14 00:00:00' AND '2015-07-14 23:59:59')
                        UNION ALL
                        (SELECT id AS ID,`send_date` `Date`,'MO' AS Type,scr_adress AS Msisdn,TEXT as text,CHAR_LENGTH(TEXT) AS QtyOfSymbols,'OK' AS `Status` FROM archiveoutgoingsms
                        WHERE 1=1 AND send_date BETWEEN '2015-06-14 00:00:00' AND '2015-07-14 23:59:59') ORDER BY `Date` ASC LIMIT 0 ,100
      

      什么时候用

      ORDER BY `id` ASC
      

      插入

      ORDER BY `Date` ASC
      

      ?

      【讨论】:

      • 不,这不一样!如果只有一个表,那将是相同的,但我必须用两个表进行查询。所以有时ID会相同,时间会不同!
      【解决方案3】:

      不知何故 mysql 在 UNION 的第一部分不使用索引 recieved_datetime

      你可以告诉MYSQL强制使用索引

      查询 1:

      EXPLAIN (SELECT id AS ID,`recieved_datetime` `Date`,'MT' AS Type,src_adress AS Msisdn,TEXT as text,CHAR_LENGTH(TEXT) AS QtyOfSymbols,'OK' AS `Status` FROM archiveincomingsms FORCE INDEX(recieved_datetime)
                    WHERE 1=1 AND recieved_datetime BETWEEN '2015-06-14 00:00:00' AND '2015-07-14 23:59:59')
                    UNION ALL
                    (SELECT id AS ID,`send_date` `Date`,'MO' AS Type,scr_adress AS Msisdn,TEXT as text,CHAR_LENGTH(TEXT) AS QtyOfSymbols,'OK' AS `Status` FROM archiveoutgoingsms
                    WHERE 1=1 AND send_date BETWEEN '2015-06-14 00:00:00' AND '2015-07-14 23:59:59') ORDER BY `Date` ASC LIMIT 0 ,100
      

      查询 2:

      (SELECT id AS ID,`recieved_datetime` `Date`,'MT' AS Type,src_adress AS Msisdn,TEXT as text,CHAR_LENGTH(TEXT) AS QtyOfSymbols,'OK' AS `Status` FROM archiveincomingsms FORCE INDEX(recieved_datetime)
                    WHERE 1=1 AND recieved_datetime BETWEEN '2015-06-14 00:00:00' AND '2015-07-14 23:59:59')
                    UNION ALL
                    (SELECT id AS ID,`send_date` `Date`,'MO' AS Type,scr_adress AS Msisdn,TEXT as text,CHAR_LENGTH(TEXT) AS QtyOfSymbols,'OK' AS `Status` FROM archiveoutgoingsms
                    WHERE 1=1 AND send_date BETWEEN '2015-06-14 00:00:00' AND '2015-07-14 23:59:59') ORDER BY `Date` ASC LIMIT 0 ,100
      

      如何使用时间戳代替 datetime 2015-06-14 00:00:00

      【讨论】:

      • 而不是 AND recieved_datetime BETWEEN '2015-06-14 00:00:00' AND '2015-07-14 23:59:59') 您可能会在另一个查询中找到此条件并使用 id之间
      • 如何使用时间戳代替 datetime 2015-06-14 00:00:00
      • 我认为时间戳在这个查询中没有什么不同!我已经尝试了很多! :( 尝试时很累 :(
      【解决方案4】:

      请提供SHOW CREATE TABLE archiveincomingsms;——比您提供的更清晰、更易读。此外,它还显示了引擎和索引。

      对于UNION,您需要

      INDEX(send_date),
      INDEX(received_date)
      

      但是,由于您正在查看一个月的数据,这可能是表格的重要部分。如果它超过表的 20%,它将执行一次表扫描(或两次扫描),这意味着 UNION 没有帮助。

      附注:而不是

                AND  send_date BETWEEN '2015-06-14 00:00:00'
                                   AND '2015-07-14 23:59:59'
      

      这样会更简洁:

                AND  send_date >= '2015-06-14'
                AND  send_date <  '2015-06-14' + INTERVAL 1 MONTH
      

      它会自动处理较短的月份和年份界限。

      表名很难区分;它们都像

      archivemiscellanyms 
      

      哦,我还没说完(现在我盯着表名发现它们是不同的)......

      ORDER BY...LIMIT添加到UNION的每个部分:

      ( SELECT ... ORDER BY received_datetime LIMIT    100 )
      UNION ALL
      ( SELECT ... ORDER BY send_date         LIMIT    100 )
      ORDER BY date                           LIMIT 0, 100
      

      如果你要“分页”(畏缩),那么对第三页执行此操作:

      ( SELECT ... ORDER BY received_datetime LIMIT      300 )
      UNION ALL
      ( SELECT ... ORDER BY send_date         LIMIT      300 )
      ORDER BY date                           LIMIT 200, 100
      

      现在索引将可用。然而,随着用户从一个页面到另一个页面,它会变得越来越慢。

      你可以可能通过“记住你离开的地方”来加快后续页面的速度。见my pagination blog

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-03-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-10
        • 1970-01-01
        相关资源
        最近更新 更多