游客发表
SQL里有个词,叫EXISTS。它不显山不露水,却几乎决定了你写的每一条查询的生死。
我见过太多人写关联查询,一上来就是IN。比如查“买过东西的客户”,张口就是WHERE id IN (SELECT customer_id FROM orders)。数据量小的时候,一切岁月静好。等订单表涨到几百万行,查询慢得像蜗牛爬,DBA的工单堆成山,你才想起EXISTS。
区别在哪儿?IN是把子查询的结果整个算出来,存成一张临时表,再拿外层数据去比对。EXISTS不一样,它不关心子查询返回什么内容,只问一句“有没有”。有就停,没有就算。像查户口,只问你家里有没有人,不问你床上躺的是谁。
举个例子。你要找出“最近30天没下过单的VIP客户”。用NOT IN写,子查询得先扫一遍订单表,把30天内的customer_id全捞出来,再去重,再比对。用NOT EXISTS写,数据库是逐行判断的——每看一个客户,就去订单表里找有没有他最近30天的记录,找到一条立刻返回“有”,找不到才继续看下一个。前者像把整条街的垃圾桶翻一遍找某个瓶盖,后者是每家每户门口问一句“你家有瓶盖吗”。
但你别急着把所有IN都改成EXISTS。这玩意儿有个坑。如果子查询的结果集特别小,而且外层表很大,IN反而可能更快,因为它能走哈希关联。EXISTS有时候会逼着数据库走嵌套循环,外层每行都要去内层探一次。所以别迷信,得看执行计划。
我印象最深的一次,是处理一个报表任务。两张表,一张是用户表,两百万行;一张是行为日志,一亿多行。业务要统计“有行为记录的用户”。同事用IN写的,跑了四十分钟没出来。我改成EXISTS,关联字段上建了索引,二十秒跑完。那哥们儿盯着屏幕愣了半天,问我是不是偷偷改了数据。
其实更微妙的是EXISTS和JOIN之间的纠缠。有时候你明明只需要判断存在性,却习惯性地JOIN上去,结果把数据行数放大了。比如一个用户买了十件商品,JOIN出来十条记录,你还得去重。EXISTS压根不关心买了多少件,只关心买没买过。这种语义上的干净,是JOIN给不了的。
还有那种“查所有没被引用过的记录”的需求。用NOT EXISTS比用LEFT JOIN加IS NULL要直观得多。后者你得先做全表关联,再过滤空值,中间那一步产生的中间结果可能大得吓人。前者直接反向扫描,逻辑上一目了然。
说句实话,很多开发写了几年SQL,还是条件反射式地敲IN和JOIN。EXISTS这个词看着有点学术,有点冷门,像是教科书里凑数的概念。但真正在脏数据、大表、慢查询里滚过几回的人,都会对它生出一种朴素的信任。它不是银弹,但确实是那种“关键时刻能救命”的工具。
你下次写查询的时候,先问自己一句:我到底是要数据,还是要判断?要数据,用JOIN;要判断,试试EXISTS。就这么简单。
友情链接