使用where子句进行慢计数查询

时间:2015-01-05 17:34:01

标签: mysql sql

我正在尝试执行计数以获取分页中的结果总数,但查询速度太慢 2.12s

+-------+
| size  |
+-------+
| 50000 |
+-------+
1 row in set (2.12 sec)

我的计数查询

select  count(appeloffre0_.ID_APPEL_OFFRE) as size  
from  ao.appel_offre appeloffre0_  
inner join ao.acheteur acheteur1_ 
on appeloffre0_.ID_ACHETEUR=acheteur1_.ID_ACHETEUR 
where 
(exists (select 1 from ao.lot lot2_ where lot2_.ID_APPEL_OFFRE=appeloffre0_.ID_APPEL_OFFRE and lot2_.ESTIMATION_COUT>=1)) 
and (exists (select 1 from ao.lieu_execution lieuexecut3_ where lieuexecut3_.appel_offre=appeloffre0_.ID_APPEL_OFFRE and lieuexecut3_.region=1)) 
and (exists (select 1 from ao.ao_activite aoactivite4_ where aoactivite4_.ID_APPEL_OFFRE=appeloffre0_.ID_APPEL_OFFRE and (aoactivite4_.ID_ACTIVITE=1))) 
and appeloffre0_.DATE_OUVERTURE_PLIS>'2015-01-01' 
and (appeloffre0_.CATEGORIE='fournitures' or appeloffre0_.CATEGORIE='travaux' or appeloffre0_.CATEGORIE='services') 
and acheteur1_.ID_ENTITE_MERE=2

解释cmd:

+----+--------------------+--------------+------+---------------------------------------------+--------------------+---------+--------------------------------+-------+--------------------------+
| id | select_type        | table        | type | possible_keys                               | key                | key_len | ref                            | rows  | Extra                    |
+----+--------------------+--------------+------+---------------------------------------------+--------------------+---------+--------------------------------+-------+--------------------------+
|  1 | PRIMARY            | acheteur1_   | ref  | PRIMARY,acheteur_ibfk_1                     | acheteur_ibfk_1    | 5       | const                          |     3 | Using where; Using index |
|  1 | PRIMARY            | appeloffre0_ | ref  | appel_offre_ibfk_2                          | appel_offre_ibfk_2 | 4       | ao.acheteur1_.ID_ACHETEUR      | 31061 | Using where              |
|  4 | DEPENDENT SUBQUERY | aoactivite4_ | ref  | ao_activites_activite_fk,ao_activites_ao_fk | ao_activites_ao_fk | 4       | ao.appeloffre0_.ID_APPEL_OFFRE |     3 | Using where              |
|  3 | DEPENDENT SUBQUERY | lieuexecut3_ | ref  | fk_ao_lieuex,fk_region_lieuex               | fk_ao_lieuex       | 4       | ao.appeloffre0_.ID_APPEL_OFFRE |     1 | Using where              |
|  2 | DEPENDENT SUBQUERY | lot2_        | ref  | FK_LOT_AO                                   | FK_LOT_AO          | 4       | ao.appeloffre0_.ID_APPEL_OFFRE |     5 | Using where              |
+----+--------------------+--------------+------+---------------------------------------------+--------------------+---------+--------------------------------+-------+--------------------------+

索引acheteur_ibfk_1是FK引用表ENTITE_MERE,因为我在where子句中有acheteur1_.ID_ENTITE_MERE=2

4 个答案:

答案 0 :(得分:1)

通过使用ON condition1 AND condition2等,您可以在joins上拥有多个条件。

SELECT COUNT(appeloffre0_.ID_APPEL_OFFRE) as size  
FROM  ao.appel_offre appeloffre0_  
JOIN ao.acheteur acheteur1_ ON appeloffre0_.ID_ACHETEUR=acheteur1_.ID_ACHETEUR 
JOIN ao.lot lot2_ ON appeloffre0_.ID_APPEL_OFFRE=lot2_.ID_APPEL_OFFRE AND lot2_.ESTIMATION_COUT>=1 
JOIN ao.lieu_execution lieuexecut3_ ON appeloffre0_.ID_APPEL_OFFRE=lieuexecut3_.ID_APPEL_OFFRE AND lieuexecut3_.ID_ACTIVITE=1
JOIN ao.ao_activite aoactivite4_ ON appeloffre0_.ID_APPEL_OFFRE=aoactivite4_.ID_APPEL_OFFRE AND aoactivite4_.ID_ACTIVITE=1
WHERE appeloffre0_.DATE_OUVERTURE_PLIS>'2015-01-01' 
AND (appeloffre0_.CATEGORIE='fournitures' OR appeloffre0_.CATEGORIE='travaux' OR appeloffre0_.CATEGORIE='services') 
AND acheteur1_.ID_ENTITE_MERE=2;

答案 1 :(得分:1)

您可以尝试:

select count(aa.ID_APPEL_OFFRE) as size  
from  (
select ID_APPEL_OFFRE, ID_ACHETEUR from ao.appel_offre appeloffre0_  
inner join ao.acheteur acheteur1_ 
on appeloffre0_.ID_ACHETEUR=acheteur1_.ID_ACHETEUR 
where appeloffre0_.DATE_OUVERTURE_PLIS>'2015-01-01' 
and (appeloffre0_.CATEGORIE in ('fournitures','travaux','services')) 
and (acheteur1_.ID_ENTITE_MERE=2)) aa
inner join ao.lot lot2_ on lot2_.ID_APPEL_OFFRE=aa.ID_APPEL_OFFRE
inner join ao.lieu_execution lieuexecut3_ on lieuexecut3_.appel_offre=aa.ID_APPEL_OFFRE
inner join ao.ao_activite aoactivite4_  on aoactivite4_.ID_APPEL_OFFRE=aa.ID_APPEL_OFFRE
where 
aoactivite4_.ID_ACTIVITE=1
and lot2_.ESTIMATION_COUT>=1
and lieuexecut3_.region=1;

但是我没有看到你的牌桌,所以我不能100%确定你不会因为连接而得到重复。

通过确保你的appeloffre0_.CATEGORIE和appeloffre0_.DATE_OUVERTURE_PLIS上有索引,也可以找到一些低调的成果。

其他应该有索引的字段是ao.lot.ID_APPEL_OFFRE,ao.lieu_execution.ID_APPEL_OFFRE和ao.ao_activite.ID_APPEL_OFFRE,以及ao.appel_offre.ID_ACHETEUR(所有已连接的字段)。

答案 2 :(得分:1)

如果还没有,我的表上会有以下索引......这些索引覆盖了查询的索引,这意味着索引具有适用的列,无需转到实际的原始数据页即可获得结果。

table           index
appel_offre     ( DATE_OUVERTURE_PLIS, CATEGORIE, ID_APPEL_OFFRE, ID_ACHETEUR )
lot             ( ID_APPEL_OFFRE, ESTIMATION_COUT )
lieu_execution  ( appel_offre, region )
ao_activite     ( ID_APPEL_OFFRE, ID_ACTIVITE )

仅在单个列上建立索引并不能真正帮助优化您要查找的内容。此外,我正在计算DISTINCT ID_APPEL_OFFRE,以防任何JOINed表有超过1条记录,它不会为您创建笛卡尔结果计数

select  
      count(distinct AOF.ID_APPEL_OFFRE) as size
   from
      ao.appel_offre AOF
         JOIN ao.acheteur ACH
            on AOF.ID_ACHETEUR = ACH.ID_ACHETEUR 
           and ACH.ID_ENTITE_MERE = 2
         JOIN ao.lot
            ON AOF.ID_APPEL_OFFRE = lot.ID_APPEL_OFFRE
           and lot.ESTIMATION_COUT >= 1
         JOIN ao.lieu_execution EX
            ON AOF.ID_APPEL_OFFRE = EX.appel_offre
            and EX.region = 1
         JOIN ao.ao_activite ACT
            ON AOF.ID_APPEL_OFFRE = ACT.ID_APPEL_OFFRE 
           and ACT.ID_ACTIVITE = 1
   where 
          AOF.DATE_OUVERTURE_PLIS > '2015-01-01'
      and (   AOF.CATEGORIE = 'fournitures' 
           or AOF.CATEGORIE = 'travaux' 
           or AOF.CATEGORIE = 'services') 

答案 3 :(得分:0)

就像@FuzzyTree在他的评论exists中说的那样,如果它不是1:1关系,则比内连接更快,因为它一找到1就会终止,而连接将获得每个匹配的行。

但解决方法是我们添加in而不是exists

where ( appeloffre0_.ID_APPEL_OFFRE IN (select lot2_.ID_APPEL_OFFRE from ao.lot lot2_  
             where    lot2_.ESTIMATION_COUT>=1)
      ) 

因此查询运行速度比存在或加入速度快。

select  count(appeloffre0_.ID_APPEL_OFFRE) as size  
from  ao.appel_offre appeloffre0_  
inner join ao.acheteur acheteur1_ 
on appeloffre0_.ID_ACHETEUR=acheteur1_.ID_ACHETEUR 
where 
( appeloffre0_.ID_APPEL_OFFRE IN (select lot2_.ID_APPEL_OFFRE from ao.lot lot2_  where    lot2_.ESTIMATION_COUT>=1)) 
and (appeloffre0_.ID_APPEL_OFFRE IN (select lieuexecut3_.appel_offre from ao.lieu_execution lieuexecut3_  where  lieuexecut3_.region=1)) 
and (appeloffre0_.ID_APPEL_OFFRE IN (select aoactivite4_.ID_APPEL_OFFRE from ao.ao_activite aoactivite4_  where aoactivite4_.ID_ACTIVITE=1 )) 
and appeloffre0_.DATE_OUVERTURE_PLIS>'2015-01-01' 
and (appeloffre0_.CATEGORIE='fournitures' or appeloffre0_.CATEGORIE='travaux' or appeloffre0_.CATEGORIE='services') 
and acheteur1_.ID_ENTITE_MERE=2