MySQL学堂之避免索引列使用OR条件

    作者:课课家更新于: 2019-02-28 21:15:34

    软考,您想通过吗?一次通过才是硬道理

      在我们使用MySQL数据库进行查询时如果通过使用包含OR条件的索引列来进行查询的话往往得出的数据量是非常大的,这就给我们的工作造成极大的不便。那么我们有什么方法来避免索引列使用包含OR条件的索引列呢?为此在本篇文章中课课家笔者就此向大家作出详细的方法介绍以供大家参考参考。

    MySQL学堂之避免索引列使用OR条件_考试认证_等级考试_MySQL_课课家

     


     

      下面笔者就OR的弊端给大家作出详细的介绍:

      selectf_crm_idfromd_dbname1.t_tbname1wheref_xxx_id=926067    and(f_mobile='1234567891'orf_phone='1234567891')limit1

      从上述查询语句我们不难看出f_mobile和f_phone两个字段都有可能存电话号码,一般我们的思路都是用OR去一条sql解决,但如果表数据量一大就很麻烦了:

    从上述查询语句我们不难看出f_mobile和f_phone两个字段都有可能存电话号码,一般我们的思路都是用or去一条sql解决,但如果表数据量一大就很麻烦了:

      我们不难发现t_tbanme1上有索引idx_id_mobile(f_xxx_id,f_mobile),idx_phone(f_phone),idx_id_email(f_id,f_email),explain的结果却使用了idx_id_email索引,有时候运气好可能走idx_id_mobilef_xxx_id。

      我们知道MySQL的每条查询,每个表上只能选择一个索引,如果我们使用了idx_id_mobile索引而恰好有一条数据因为有limit1,那么我们则很快得到结果,但是如果f_mobile没有数据,那么f_phone字段只能在f_id条件下挨个查找并扫描12w行。

      


     

      下面是优化sql的两种方法介绍(注意f_mobile,f_phone上都要有相应的索引):

      Method 1

      (selectf_crm_idfromd_dbname1.t_tbname1wheref_xxx_id=926067andf_mobile='1234567891'limit1)    UNIONALL    (selectf_crm_idfromd_dbname1.t_tbname1wheref_xxx_id=926067andf_phone='1234567891'limit1)

      Method 1

      我们不难发现两条独立的sql都能用上索引,分查询各自limit。如果都有结果集返回我们则随便取一条就可以了。

      


     

      下面笔者给大家介绍第二种优化办法。如果查询特别频繁且无缓存,改成单独的sql执行,比如大部分号码值都在f_mobile上,那我们可以通过先执行分sql1,如果判断有结果则结束,判断没有结果再执行分sql2。这种方法能够有效减少数据库查询速度从而让代码去处理更多的事情。

      Method 2

       sql1=selectf_crm_idfromd_dbname1.t_tbname1wheref_xxx_id=926067andf_mobile='1234567891'limit1;    sq1.execute();    ifnoresultsql1:    sql1=selectf_crm_idfromd_dbname1.t_tbname1wheref_xxx_id=926067andf_phone='1234567891'limit1;    sql1.execute();

      


     

      另外如果是复杂一点的场景则不止返回一条记录那么简单,limit2:

      selecta.f_crm_idfromd_dbname1.t_tbname1asa    where(a.f_create_time>from_Unixtime('1464397527')ora.f_modify_time>from_unixtime('1464397527'))    limit0,200

      遇到这种情况时我们则需要对【Method 1】、【Method 2】进行修改,因为f_create_time,f_modify_time都可能均满足判断条件,如此一来就会返回重复的数据。

      Method 1】的修改:

      (selecta.f_crm_idfromd_dbname1.t_tbname1asa    wherea.f_create_time>from_unixtime('1464397527')    limit0,200)    UNIONALL    (selecta.f_crm_idfromd_dbname1.t_tbname1asa    wherea.f_modify_time>from_unixtime('1464397527')anda.f_create_time<=from_unixtime('1464397527')    limit0,200)

      此时部分人或许会说把UNION ALL改成UNION不就去重了吗?如果说查询比较频繁或者limit比较大,数据库还是会有压力,所以我们还需要做trade off。这种情况更多还是适合Method 2,包括有可能需要ORder by limit情况。

      Method 2】的修改:

      sql1=(selecta.f_crm_idfromd_dbname1.t_tbname1asawherea.f_create_time>from_unixtime('1464397527')limit0,200);    sql1.execute();    sql1_count=sql1.result.count    ifsql1_count<200:    sql2=(selecta.f_crm_idfromd_dbname1.t_tbname1asawherea.f_modify_time>from_unixtime('1464397527')anda.f_create_time<=from_unixtime('1464397527')limit0,(200-sql1_count));    sql2.execute();    final_result=paste(sql1,sql2);

      PS:相同字段OR可改成in,比如f_id=1ORf_id=100->f_idin(1,100)。上述优化情景都是存储引擎在InnoDB情况下,而在MyISAM却有所不同。

     


     

      以上就是避免索引列使用OR条件的方法介绍,如果大家觉得笔者的方法可以的话不妨收藏一下吧。另外如果大家觉得笔者的文章中有不足的地方时笔者欢迎大家对本文章中出现的讲解提出自己的个人建议,对出现错误的地方提出批评,笔者会诚心接纳大家的建议和批评,并根据大家提出的建议和批评作出相应的修改工作。如果大家还想浏览更多相关的内容欢迎到网 站的IT文库浏览文章哦!

课课家教育

未登录

1