Mysql计数给了我一个非常糟糕的表现,我做错了吗?

时间:2013-08-27 02:16:06

标签: mysql performance count

当我想得到一个左连接SQL的计数时,它花了我很长时间, 我在1分钟后取消了查询,但没有得到结果。

我有两张桌子。 一个是客户,它看起来像:

----------------顾客---------------

  `ID` int(11) NOT NULL AUTO_INCREMENT,

  `drpc` int(10) DEFAULT NULL,

  `VIN` varchar(60) COLLATE utf8_bin DEFAULT NULL,

  `cph` varchar(30) COLLATE utf8_bin DEFAULT NULL,

  //... another 60+ columns here

 `invalid` int(1) DEFAULT NULL,

  PRIMARY KEY (`ID`),

  KEY `index_drpc_cph` (`drpc`,`cph`),

  KEY `index_drpc_vin` (`drpc`,`VIN`),

  KEY `index_drpc_invalid` (`drpc`,`invalid`),

  KEY `index_cph` (`cph`)

另一个是修复,它看起来像:

------------- ----------------修复

`ID` int(11) NOT NULL AUTO_INCREMENT,

  `drpc` int(10) NOT NULL,

  `cph` varchar(10) DEFAULT NULL,

  `czbh` varchar(15) DEFAULT NULL,

  `gdh` varchar(12) DEFAULT NULL,

  `kdrq` date DEFAULT NULL,

  // ... another 20+ columns here

  `invalid` int(1) DEFAULT '0',

  PRIMARY KEY (`ID`),

  KEY `gmrepair_cph` (`cph`),

  KEY `gmrepair_czbh` (`czbh`),

  KEY `gmrepair_gdh` (`gdh`),

  KEY `gmrepair_drpc_kdrq` (`drpc`,`kdrq`),

  KEY `index_drpc_invalid` (`drpc`,`invalid`),

  KEY `index_drpc_cph` (`drpc`,`cph`)

两个表都有一个字段:'cph'。

最初的要求是:对于给定的 drpc ,获取客户中存在的 cph 数据,但修复中不存在强>

我的sql语句如下所示:

SELECT * FROM customer c LEFT JOIN 
( SELECT cph FROM repair b WHERE b.drpc=77) r ON c.cph = r.cph 
WHERE c.drpc = 76 AND r.cph IS NULL 

以下是解释结果:

enter image description here

顺便说一句, 对于修复表中的drpc = 77,有大约20k的记录;

对于客户表中的drpc = 76,大约有6万条记录。

两个表的存储都是:InnoDB。

执行上面的sql大约需要3秒钟。

但是,当我想得到上面提到的sql的计数时,我需要很长时间。即使在60秒内也无法完成。

我不确定问题是什么。 能不能给我一些指示,万分感谢!

5 个答案:

答案 0 :(得分:0)

查看计划的解释总是有帮助的。看起来drpc, cph上的索引应该用于查询。

但是,如果您的基本查询有效,那么这可能会为您带来更好的效果。

select count(*)
from (SELECT *
      FROM customer c LEFT JOIN
           (SELECT distinct cph
            FROM repair b
            WHERE b.drpc=77
           ) r
           ON c.cph = r.cph 
      WHERE c.drpc = 76 AND r.cph IS NULL
     ) t;

编辑:

您可以通过这样的方式强制执行计划:

select count(*)
from customer c
where c.drpc = 76 and
      not exists (select 1 from repair r where r.drpc = 77 and r.cph = c.cph);

答案 1 :(得分:0)

尝试左外连接而不是左连接。

SELECT C.*
FROM Customer C
LEFT OUTER JOIN (SELECT cph from 
        FROM Repair WHERE drpc = 77)r  ON C.cph = r.cph
WHERE C.drpc = 76 AND R.cph IS NULL

答案 2 :(得分:0)

我的理解是您提供的查询:

SELECT * FROM customer c LEFT JOIN 
( SELECT cph FROM repair b WHERE b.drpc=77) r ON c.cph = r.cph 
WHERE c.drpc = 76 AND r.cph IS NULL 

应该与简单的左连接相同(这是计数版本):

select count(*) from customer c
where c.drpc = 76 and c.cph not in (
    select cph from repair where drpc = 77
)

这第二个查询也花了太长时间吗?

答案 3 :(得分:0)

我不明白为什么其他人没有提及,但查询中的子查询不允许有效使用索引,实际上你在一个20k行的无索引表上保持连接。

对于查询,您需要2个索引: (drpc,cph)关于客户和(cph,drpc)的维修(记住订单,你还没有)。

然后你需要重写查询:

SELECT COUNT(*)
FROM  customer c
LEFT JOIN repair r ON c.chp = r.chp AND r.drpc = 77
WHERE c.drpc = 76 AND r.chp IS NULL;

答案 4 :(得分:0)

我想我找到了真正的伎俩。

这是因为左连接字段cphvarchar(10),导致左连接作业时非常慢。

我在两个表上创建了一个新列:hash_cph numberic(30,0),然后以这种方式将cph转换为某些MD5哈希数: UPDATE customer SET hash_cph = CONV(RIGHT(MD5(cph),16),16,10)

所以我现在可以在新创建的列hash_cph上应用左连接,它会快得多。 最终的SQL看起来像: SELECT COUNT(*) FROM customer c LEFT JOIN repair r ON c. {hash_cph {1}}

顺便说一句,我还在= r.hash_cph AND r.drpc = 32 WHERE c.drpc = 1 AND r.hash_cph IS NULL;为两个表添加了索引。

感谢大家的帮助!!