关于SQL中IN与=ANY、NOT IN与<>ALL等价性的技术问询
好问题!这两个是SQL里很容易混淆的点,我来给你掰扯清楚:
=ANY 和 IN 是否完全等价? 没错,在你给出的子查询场景下,这两个写法完全等价。比如你写的:
SELECT * FROM HOUSES WHERE TYPE = ANY (SELECT TYPE FROM TYPES WHERE CAT_ID = 'PUBLIC')
和
SELECT * FROM HOUSES WHERE TYPE IN (SELECT TYPE FROM TYPES WHERE CAT_ID = 'PUBLIC')
执行逻辑和结果完全一致。
其实不止子查询,面对常量列表时,二者也能达到同样效果,只是写法略有不同。比如TYPE IN ('APARTMENT', 'HOUSE'),换成=ANY的话,在支持数组的数据库(比如PostgreSQL)里可以写成TYPE = ANY(ARRAY['APARTMENT', 'HOUSE']),核心逻辑都是判断TYPE是否等于列表/子查询里的任意一个值。
唯一要注意的是,IN是SQL标准里更常用的写法,可读性更强;而=ANY在一些复杂场景(比如结合数组操作)下会更灵活。
NOT IN 和 <>ALL 是否等价? 这个要分情况说,但核心结论是:在子查询不包含NULL值的情况下,二者完全等价;但如果子查询里存在NULL,二者的行为虽然一致,却都会触发一个容易踩的坑——导致查询返回空结果。
举个例子,如果TYPES表的TYPE列包含NULL值,那么:
SELECT * FROM HOUSES WHERE TYPE NOT IN (SELECT TYPE FROM TYPES WHERE CAT_ID = 'PUBLIC')
和
SELECT * FROM HOUSES WHERE TYPE <> ALL (SELECT TYPE FROM TYPES WHERE CAT_ID = 'PUBLIC')
都会返回空结果。原因是SQL里NULL的比较结果是UNKNOWN:当TYPE不等于子查询里的所有非NULL值时,TYPE IN (...)的结果是UNKNOWN,NOT UNKNOWN仍然是UNKNOWN,所以不会匹配任何行;<>ALL则要求TYPE不等于子查询里的每一个值,其中和NULL的比较是UNKNOWN,整个表达式结果也是UNKNOWN,同样不会返回行。
如果子查询里没有NULL,那二者就完全一样了,都是判断TYPE不等于子查询里的所有值。
不过这里额外提醒一句:如果你的子查询可能返回NULL,最好用NOT EXISTS替代NOT IN或<>ALL,因为NOT EXISTS会忽略NULL的影响,逻辑更符合直觉。
内容的提问来源于stack exchange,提问作者Joe DiNottra

