SQL秒写,涨薪咔嚓!前不久,公司新来的实习生因为写错了SQL,把数据库给炸了。图片来自Pexels。新来的实习生小杨写了一条SQL语句:SELECTwx_idfrom`user`WHEREwx_id=2等小杨下班迫不及待要回家的时候,隔壁王经理一把抓住了小杨,用EXPLAIN命令对小杨进行了教育。杨,小杨流下了没受过教育的眼泪。在这条SQL语句中,wx_id是有索引的,但是王经理查到的结果是这样的:王经理的学历小杨仔细查看了关键字段,显示为Null。很明显这条SQL语句没有使用索引。小杨心想,“哎呀,我又写错了SQL语句,现在又要面对运维和经理的混双了,不行,我得马上改这条SQL语句,让我想想是哪里出了问题“!小杨我脑袋乱撞,仔细回想了一下表结构,突然发现wx_id字段是varchar类型的,我自己查询的时候没有加引号。小杨一把夺过经理手上的键盘,给wx_id查询条件加上了引号。结果:果然,这条SQL语句开始丢索引了。小杨得意洋洋,认为自己解决了一个大bug。经理微微一笑,问道:“你知道加引号后为什么不留下索引吗?如果是int类型的字段,查询的时候需要加引号吗?为什么?”晚饭来了,小杨被问到呆在哪儿,答不上来。小杨经过研究发现,如果字段是varchar类型,则必须在等号右边加上索引;什么?小杨说的你别信,有图有真相。(bonus字段的类型是int)真值图但是得出结论后,还是不能回答经理的三个连续问题。在MySQL查询中,当查询条件左右两边的类型不匹配时,会发生隐式转换:也就是说SELECTwx_idfrom`user`WHEREwx_id=2等价于SELECTwx_idfrom`user`WHERECAST(wx_idASsignedint)=2一旦对索引字段进行了函数操作,MySQL就会放弃使用该索引。因此,如果字段是varchar类型,必须在等号右边加上引号才能使用索引,否则MySQL会因为隐式转换而放弃使用索引。那么为什么int可以带引号也可以不带引号?那是因为int类型的数字只有2可以转换成'2',这是唯一确定的。因此,虽然需要进行隐式转换,但并不影响索引的使用。小杨问:“你还能告诉我一些关于隐式转换的知识吗?”.ForNULL<=>NULL,theresultistrue.Noconversionisneeded.Ifbothargumentsinacomparisonoperationarestrings,theyarecomparedasstrings.Ifbothargumentsareintegers,theyarecomparedasintegers.Hexadecimalvaluesaretreatedasbinarystringsifnotcomparedtoanumber.IfoneoftheargumentsisaTIMESTAMPorDATETIMEcolumnandtheotherargumentisaconstant,theconstantisconvertedtoatimestampbeforethecomparisonisperformed.ThisisdonetobemoreODBC-friendly.NotethatthisisnotdonefortheargumentstoIN()!Tobesafe,alwaysusecompletedatetime,date,ortimestringswhendoingcomparisons.Forexample,toachievebestresultswhenusingBETWEENwithdateortimevalues,useCAST()toexplicitlyconvertthevaluestothedesireddatatype.来自attable或table的单行子查询不被视为常量。例如,ifa子查询返回一个整数以进行比较redtoaDATETIMEvalue,thecomparisonisdoneastwointegers.Theintegerisnotconvertedtoatemporalvalue.TocomparetheoperandsasDATETIMEvalues,useCAST()toexplicitlyconvertthesubqueryvaluetoDATETIME.Ifoneoftheargumentsisadecimalvalue,comparisondependsontheotherargument.Theargumentsarecomparedasdecimalvaluesiftheotherargumentisadecimalorintegervalue,orasfloating-pointvaluesiftheotherargumentisafloating-pointvalue.Inallothercases,theargumentsarecomparedasfloating-point(real)numbers.贴心的我帮你们翻译成了中文:1,两个参数当其中至少一个为NULL时,比较结果也为NULL。例外的是当使用<=>比较两个NULL时,它会返回1。在这两种情况下,都不需要进行类型转换。2.两个参数都是字符串。会按照字符串进行比较,不进行类型转换3,两个参数都是整数,按照整数进行比较,不进行类型转换4,十六进制值与非数字进行比较时,将其视为二进制字符串5、一个参数是TIMESTAMP或DATETIME,另一个参数是常量,常量会转为timestamp6,一个参数是decimal类型,如果另一个参数是decimal或integer,则将integer转为decimal进行比较,否则如果一个参数是浮点数,则将小数转为浮点数进行比较7.其他所有情况,将两个参数转为浮点数进行比较,然后共享一个隐式转换的坑:偶尔会删除一些Unknowndata吗?mysql>select*fromtest;+----+--------+----------+|id|name|密码|+----+------+------------+|1|test1|password1||2|test2|密码2||3|aaa|aaaa||4|55aaa|55aaaa||5|1212|aaa||6|1212a|aaa|+----+------+----------+6rowsinset(0.00sec)mysql>select*fromtestwherename=1212;+----+--------+----------+|id|name|密码|+----+--------+---------+|5|1212|aaa||6|1212a|aaa|+----+-------+------------+2rowsinset,5warnings(0.00sec)mysql>select*fromtestwherename='1212';+----+------+---------+|id|name|password|+----+-----+--------+|5|1212|aaa|+----+------+----------+1rowinset(0.00sec)上面这个例子的初衷是查询id为5的记录,结果查询到的记录为id6我想解释什么?有时候我们的数据库表中有些列是varchar类型的,但是存储的值是纯数字的字符串值,比如'1123'。有些同学不习惯加引号。这样,在执行Select、Update或Delete时,可能会操作更多的数据。所以不要忘记应该在什么地方加上引号。总之,隐式类型转换有命中索引失败的风险。在高并发、大数据量的情况下,错过索引的后果可不仅仅是运维和管理者的混双!并编写SQL和EXPLAIN!作者:isysc1责任编辑:陶佳龙来源:转载自微信公众号麦儿嘟嘟嘟(ID:maer_duduqi)
