优化MySQL查询-多列搜索条件

问题描述 投票:0回答:2

我正在使用MySQL 5.7.25,这是我要优化的查询:

SELECT a.contract,
       a.phone_number_1,
       a.phone_number_2,
       a.phone_number_3,
       a.phone_number_4,
       a.phone_number_5
  FROM tempdb.customer_crm a
 WHERE CHAR_LENGTH(a.contract) = 12
   AND (
         a.contract in (SELECT contract_final FROM tempdb.relevant_contracts)
         OR a.phone_number_1 in (SELECT phone_number FROM tempdb.relevant_numbers_1)
         OR a.phone_number_2 in (SELECT phone_number FROM tempdb.relevant_numbers_2)
         OR a.phone_number_3 in (SELECT phone_number FROM tempdb.relevant_numbers_3)
         OR a.phone_number_4 in (SELECT phone_number FROM tempdb.relevant_numbers_4)
         OR a.phone_number_5 in (SELECT phone_number FROM tempdb.relevant_numbers_5)
       );

customer_crm表在5列中有5个不同的电话号码。我需要过滤表relevant_numbers中存在5个电话号码中的任何一个的所有记录。我已经制作了5张表relevant_numbers的副本,因为我只能使用TEMPORARY表(在MySQL中不能多次打开)。中的记录数:

  • customer_crm:8000万
  • relevant_numbers:63千
  • relevant_contracts:93千
  • 查询结果:10万

此查询花费的时间太长。我使用(电话号码长度条件)节省了几分钟:

SELECT a.contract,
       a.phone_number_1,
       a.phone_number_2,
       a.phone_number_3,
       a.phone_number_4,
       a.phone_number_5
  FROM tempdb.customer_crm a
 WHERE CHAR_LENGTH(a.contract) = 12
   AND (
         a.contract in (SELECT contract_final FROM tempdb.relevant_contracts)
         OR (CHAR_LENGTH(a.phone_number_1) > 9 AND a.phone_number_1 in (SELECT phone_number FROM tempdb.relevant_numbers_1))
         OR (CHAR_LENGTH(a.phone_number_2) > 9 AND a.phone_number_2 in (SELECT phone_number FROM tempdb.relevant_numbers_2))
         OR (CHAR_LENGTH(a.phone_number_3) > 9 AND a.phone_number_3 in (SELECT phone_number FROM tempdb.relevant_numbers_3))
         OR (CHAR_LENGTH(a.phone_number_4) > 9 AND a.phone_number_4 in (SELECT phone_number FROM tempdb.relevant_numbers_4))
         OR (CHAR_LENGTH(a.phone_number_5) > 9 AND a.phone_number_5 in (SELECT phone_number FROM tempdb.relevant_numbers_5))
       );

仍然需要大约10分钟。我尝试使用EXISTS条件而不是IN,它花费的时间甚至更长。我也尝试使用左连接,这也需要更长的时间。所有列均单独索引。

任何帮助将不胜感激。谢谢。

mysql query-optimization
2个回答
1
投票

OR是性能杀手。 IN ( SELECT ... )也是如此。

按现状查询将对80M行进行全表扫描,并在临时表中进行查找。如果您要为临时表建立索引,那么这些次要查询将只有1行,否则将只有63K行-总共将进行25个trillion查找。它可能在今年完成。

计划A:OR转换为UNION

    (  SELECT  cc.id
            FROM  tempdb.customer_crm AS cc
            JOIN  tempdb.relevant_contracts AS rc
            WHERE  cc.contract = rc.contract 
    )  UNION  
    (  SELECT  cc.id
            FROM  tempdb.customer_crm AS cc
            JOIN  tempdb.relevant_numbers_1 AS rn
            WHERE  cc.phone_number_1 = rn.phone_number 
    )  UNION
    (  SELECT  cc.id
            FROM  tempdb.customer_crm AS cc
            JOIN  tempdb.relevant_numbers_2 AS rn
            WHERE  cc.phone_number_2 = rn.phone_number 
    )  UNION
    (  SELECT  cc.id
            FROM  tempdb.customer_crm AS cc
            JOIN  tempdb.relevant_numbers_3 AS rn
            WHERE  cc.phone_number_3 = rn.phone_number 
    )  UNION  
    (  SELECT  cc.id
            FROM  tempdb.customer_crm AS cc
            JOIN  tempdb.relevant_numbers_4 AS rn
            WHERE  cc.phone_number_4 = rn.phone_number 
    )  UNION  
    (  SELECT  cc.id
            FROM  tempdb.customer_crm AS cc
            JOIN  tempdb.relevant_numbers_5 AS rn
            WHERE  cc.phone_number_5 = rn.phone_number 
    )

我假设idPRIMARY KEYcustomer_crm。您需要在customer_crm上使用这些索引:

INDEX(contract, id)
INDEX(phone_number_1, id)
INDEX(phone_number_2, id)
INDEX(phone_number_3, id)
INDEX(phone_number_4, id)
INDEX(phone_number_5, id)

将以上查询用作子查询,将JOIN返回到customer_crm以获取您真正需要的任何列。

将进行大约一百万次操作-减少多]。

length = 12的检查可能会在以后引起轻微的麻烦。

计划B:

请勿使用5列。

通常,将一堆东西散布在多列中或打包在一起放在一列中通常是一个糟糕的模式设计。相反,要有另一个具有(至少)2列的表:numberid联接回主表。

使用INDEX(number),它有5 * 80M行无关紧要。

计划C:

在创建临时表之前,您是否需要备份?其他优化也是可能的。

2
投票

customer_crm表在5列中有5个不同的电话号码。我需要过滤所有在表related_numbers中存在5个电话号码的记录。

© www.soinside.com 2019 - 2024. All rights reserved.