SQL标准不支持多列子查询直接用于LIKE模糊匹配,合法路径仅有两种:一是行构造器(col1, col2) IN (SELECT a, b FROM t)实现精确等值匹配,要求列数、顺序、类型严格一致且不支持NULL;二是用EXISTS子查询将多列组合条件(如c.name LIKE CONCAT('%', o.keyword, '%') AND c.city LIKE CONCAT('%', o.city_part, '%'))统一置于WHERE中,通过关联字段绑定主表,兼顾模糊匹配与动态判断。

SQL标准不支持把多列子查询直接套在LIKE或函数里做模糊匹配,也不允许用(a, b) IN (SELECT x, y FROM t)配合通配符——这类写法要么报错,要么语义错乱。真正能跑通的,只有两种路径:行构造器精确匹配,或EXISTS驱动的多字段动态判断。
用(col1, col2) IN (SELECT a, b FROM t)做等值匹配
这是最接近“多列一起查”的合法语法,但仅限完全相等,不能加LIKE、不能拼接、不能有NULL参与。
- 必须确保左右两边列数、顺序、类型严格一致;写成
(a, b) IN (SELECT b, a FROM t)就匹配不上 - MySQL 5.7 及更早版本不支持该语法,会报
ERROR 1241 (21000): Operand should contain 1 column(s) - 只要子查询任意一行的
b为NULL,那一整行就判为UNKNOWN,不会被选中 - PostgreSQL/SQL Server/Oracle 都支持,但别指望它走索引——除非你建了覆盖
(a, b)的复合索引,且查询条件满足最左前缀原则
用EXISTS替代多列模糊或组合条件
当你要的是“某个用户既在活跃名单里,又最近下单过,还地址匹配”,或者“名字和城市都要模糊包含关键词”,EXISTS是唯一可靠选择。
- 把所有条件塞进子查询的
WHERE里,用关联字段绑定主表,比如c.id = o.customer_id -
LIKE必须作用在单列上:c.name LIKE CONCAT('%', o.keyword, '%'),不能写成(c.name, c.city) LIKE ... - 子查询里别漏
SELECT 1——写SELECT *会多传数据,没意义 - 如果子查询里用了
OR或IN,注意加括号,否则逻辑优先级可能出错,比如status = 'active' OR type = 'vip' AND created_at > '2026-01-01'实际等价于status = 'active' OR (type = 'vip' AND created_at > '2026-01-01')
为什么CONCAT(a, '|', b) LIKE '%xxx|yyy%'要慎用
看似能绕开多列限制,实则埋雷:它根本没法走索引,空值处理麻烦,分隔符冲突风险高。
-
CONCAT(NULL, '|', 'b')结果是NULL,整行直接丢失,得全用COALESCE(a, '')兜底 - 分隔符
|如果原始数据里真有,就会误匹配,比如a='foo|bar'和b='x'拼成'foo|bar|x',搜'bar|x'就命中了,但语义不对 - 哪怕加了索引,数据库也几乎不可能对
CONCAT()结果走索引扫描,99%情况触发全表扫描 - PostgreSQL可用
TO_TSVECTOR做全文检索替代,但那是另一套机制,不适用于简单字段拼接场景
真正难的不是写出能跑的SQL,而是想清楚业务到底要什么匹配逻辑:是要求两列值“同时满足”某个模糊模式,还是“任一列满足即可”,或是“组合后整体像一个字符串”。不同语义对应完全不同的技术路径,硬套语法只会让问题更模糊。

















