Mysql 优化查询和子查询

Mysql 优化查询和子查询,mysql,Mysql,目前我有: insert into temp select * from myTable where (called_phone in ( select number1 from ( SELECT * FROM ( SELECT called_phone as number1, count(*) as conto FROM myTable GROUP BY called_p

目前我有:

insert into temp select  * from myTable where (called_phone in
(
    select number1 from
    (
        SELECT *  FROM
        (
            SELECT called_phone as number1, count(*) as conto
            FROM myTable
            GROUP BY called_phone

        ) AS subquery
        union 
        SELECT * FROM
        (
            SELECT calling_phone as number1, count(*) as conto
            FROM myTable
            GROUP BY calling_phone

        ) AS subquery1
    )as subquery3
    GROUP BY number1
    having sum(conto) > 4000
))
or
(calling_phone in 
    (
        select number1 from
        (
            SELECT *  FROM
            (
                SELECT called_phone as number1, count(*) as conto
                FROM myTable
                GROUP BY called_phone

            ) AS subquery
            union 
            SELECT * FROM
            (
                SELECT calling_phone as number1, count(*) as conto
                FROM myTable
                GROUP BY calling_phone

            ) AS subquery1
        )as subquery3
        GROUP BY number1
        having sum(conto) > 4000
    )
);
我有两列(called和calling phone),其中我必须检查每个号码的所有发生率,并将计数(*)>4000的所有号码存储在另一个表中,将两列中的发生率相加。这个查询的问题是我做了2次子查询,子查询本身扫描了2次MyTable。我想把子查询存储在一个临时表中,然后扫描它。这是最好的方法吗?你有什么建议

编辑:我使用MySQL 5.7和MyISAM作为引擎

EDIT2:尝试了以下操作:

create table test (`number1` VARCHAR(255) not NULL, primary key (`number1`));

insert into test(number1) select number1 from 
    (
        SELECT *  FROM
        (
            SELECT called_phone as number1, count(*) as conto
            FROM myTable
            GROUP BY called_phone

        ) AS subquery
        union 
        SELECT * FROM
        (
            SELECT calling_phone as number1, count(*) as conto
            FROM myTable
            GROUP BY calling_phone

        ) AS subquery1
    )as subquery3
    GROUP BY number1
    having sum(conto) > 4000;



insert into temp select  * from myTable where (called_phone in
(
    select number1 from test
    ))
or
(calling_phone in 
    (
        select number1 from test
));

drop table test;
但这要慢得多(至少在我的测试数据上是如此,这是一个包含14条记录的表)

  • 第一种方法需要350毫秒到380毫秒
  • 第二次进近需要800毫秒到1.8秒
最终编辑:我正在编写产生最佳结果的查询,它来自@Daniel E的答案

insert into temp
SELECT t1.* myTable t1 
INNER JOIN 
 (
   select number1 from
        (
            SELECT *  FROM
            (
                SELECT called_phone as number1, count(*) as conto
                FROM myTable
                GROUP BY called_phone

            ) AS subquery
            union 
            SELECT * FROM
            (
                SELECT calling_phone as number1, count(*) as conto
                FROM myTable
                GROUP BY calling_phone

            ) AS subquery1
        )as subquery3
        GROUP BY number1
        having sum(conto) > 4000
 ) t2 ON (t2.number1 = t1.called_phone
        OR 
         t2.number1 = t1.calling_phone)
  • 第一种方法需要350毫秒到380毫秒
  • 第二次进近需要800毫秒到1.8秒
  • 最后一次进近需要315到335毫秒

    • 您可以将。。。在递归语句中经常使用的策略

      (请原谅我的错误意图或支架)

      尝试:

      或者更快的可能是:

      insert into tabletemp 
          select number1 from (
              SELECT called_phone as number1
              FROM myTable
              union all
              SELECT calling_phone as number1
              FROM myTable ) subquery
          GROUP BY number1
          having count(number1) > 4000 ) ;
      
      insert into temp 
      select  * 
      from myTable as mt 
      join tabletemp tbt on (mt.called_phone = tbt.number1 or mt.calling_phone = tbt.number1);
      
      drop table tabletemp;
      

      正如您似乎理解的那样,插入到我将重写select,而不是使用IN和OR,我使用1个内部联接:

      SELECT t1.* FROM myTable t1 
      INNER JOIN 
       (
         select number1 from
              (
                  SELECT *  FROM
                  (
                      SELECT called_phone as number1, count(*) as conto
                      FROM myTable
                      GROUP BY called_phone
      
                  ) AS subquery
                  union 
                  SELECT * FROM
                  (
                      SELECT calling_phone as number1, count(*) as conto
                      FROM myTable
                      GROUP BY calling_phone
      
                  ) AS subquery1
              )as subquery3
              GROUP BY number1
              having sum(conto) > 4000
       ) t2 ON (t2.number1 = t1.called_phone
              OR 
               t2.number1 = t1.calling_phone)
      

      MySQL不支持这种语法,至少可能不是OP使用的版本。是的,事实上我有一个错误。编辑主要帖子是因为我忘了指定我的引擎和mysqlversion@TimBiegeleisen真正地什么东西不适合它?您只能在“with…”表上使用select吗?请尝试在MySQL上运行您的查询。在发布代码之前是否对其进行了测试?with不是MySQL 5.7的有效语法。我想它是在MySQL 8.0中添加的,我不明白。你只是打断了我的提问。我需要记下所有的电话号码。这就是为什么我有一个OR,这就是我的问题所在,很抱歉我犯了错误——我编辑了我的答案,现在可能更好了。这不起作用。我的子查询是正确的,而您返回的结果是空的。我感谢您的努力,现在它正在工作,但是您的查询比我的慢2-3倍。我在一个非常小的数据库中测试它(myTable中约14k条记录)看看这个,当我结合两个内部连接时,从你的答案我得到一个空表,我认为这是因为(但我可能错了)你永远不会找到两个连接之间的交集。数字每次只能出现在一列中,不能同时出现在两列中。我需要做的是将所有出现次数超过4000次的数字(这个数字目前并不重要)和两列中的所有出现次数相加。这是可行的,但我最初的查询似乎仍然是最快的方法。你的查询大约需要520毫秒,而我的查询需要370毫秒。我想我做得比我已经做的要好得多。我发现,如果你删除distinct,效果会更好。我将您的答案标记为已接受的答案,但您应该修改itA“或”中的一个连接是相当昂贵的,这就是为什么我对我的答案不满意,但我不知道我们可以做什么。我不确定你是否应该接受我的答案。你的测试有什么消息吗?不幸的是,我对mu测试数据有一些问题。我需要在更大的桌子上测试它们。但我需要先完成其他事情
      insert into tabletemp 
          select number1 from (
              SELECT called_phone as number1
              FROM myTable
              union all
              SELECT calling_phone as number1
              FROM myTable ) subquery
          GROUP BY number1
          having count(number1) > 4000 ) ;
      
      insert into temp 
      select  * 
      from myTable as mt 
      join tabletemp tbt on (mt.called_phone = tbt.number1 or mt.calling_phone = tbt.number1);
      
      drop table tabletemp;
      
      SELECT t1.* FROM myTable t1 
      INNER JOIN 
       (
         select number1 from
              (
                  SELECT *  FROM
                  (
                      SELECT called_phone as number1, count(*) as conto
                      FROM myTable
                      GROUP BY called_phone
      
                  ) AS subquery
                  union 
                  SELECT * FROM
                  (
                      SELECT calling_phone as number1, count(*) as conto
                      FROM myTable
                      GROUP BY calling_phone
      
                  ) AS subquery1
              )as subquery3
              GROUP BY number1
              having sum(conto) > 4000
       ) t2 ON (t2.number1 = t1.called_phone
              OR 
               t2.number1 = t1.calling_phone)