sql server多字段字符串模糊查询存在字段null值查询失效问题

在 SQL Server 中进行多字段字符串模糊查询(如 WHERE col1 LIKE '%关键词%' OR col2 LIKE '%关键词%')时,如果某些字段为 NULL,会导致整个 OR 条件中该部分返回 UNKNOWN(非 TRUE/FALSE),从而漏掉本应匹配的记录,造成"查询失效"。

在 SQL 中,任何与 NULL 的比较(包括 LIKE)结果都是 UNKNOWN。

WHERE 子句只返回 TRUE 的行,UNKNOWN 和 FALSE 都会被过滤掉。

因此,如果 col1 IS NULL,那么 col1 LIKE '%xxx%' → UNKNOWN → 该行被排除,即使 col2 匹配!

sql 复制代码
-- 表数据:
-- id | name     | remark
-- 1  | 'Apple'  | NULL
-- 2  | NULL     | 'Banana'

-- 查询:找包含 'Apple' 的记录
SELECT * FROM t 
WHERE name LIKE '%Apple%' OR remark LIKE '%Apple%';
-- 结果:✅ 返回 id=1

-- 查询:找包含 'Banana' 的记录
SELECT * FROM t 
WHERE name LIKE '%Banana%' OR remark LIKE '%Banana%';
-- 结果:❌ **返回空!** 因为 name IS NULL → name LIKE ... = UNKNOWN,remark 虽匹配但 OR 整体未生效?
-- 实际上:remark LIKE '%Banana%' 是 TRUE,所以应该返回 id=2 ✅

-- ❗ 真正的问题场景是:
-- 当你搜索的关键词在非 NULL 字段中不存在,而另一个字段是 NULL 时,可能误判。
-- 但更常见的是:开发者误以为 NULL 会"跳过",其实不会影响其他 OR 条件。

上述 OR 查询 本身是正确的!只要有一个条件为 TRUE,整行就会被返回。

真正的问题通常出现在以下场景:

场景 1:使用 AND 而不是 OR

sql 复制代码
-- 错误:要求两个字段都匹配(但其中一个为 NULL)
WHERE name LIKE '%xxx%' AND remark LIKE '%xxx%'
-- 如果 remark IS NULL,则 AND 整体为 UNKNOWN → 行被过滤

场景 2:动态拼接 SQL 时未处理 NULL

sql 复制代码
// Java 伪代码
String sql = "SELECT * FROM t WHERE 1=1";
if (keyword != null) {
    sql += " AND (name LIKE '%" + keyword + "%' OR remark LIKE '%" + keyword + "%')";
}

这本身没问题,但如果字段有 NULL,不影响 OR 逻辑。

场景 3:误用 NOT LIKE + NULL

sql 复制代码
-- 想排除包含 'test' 的记录
WHERE name NOT LIKE '%test%'
-- 如果 name IS NULL,条件为 UNKNOWN → 行被排除!
-- 但你可能希望保留 NULL 值的行

正确解决方案:安全处理 NULL

虽然 OR 查询本身对 NULL 是安全的,但为了代码健壮性和避免逻辑误解,建议显式处理 NULL。

方法 1:使用 ISNULL() 或 COALESCE() 替换 NULL

sql 复制代码
-- 将 NULL 视为空字符串 '',确保 LIKE 可执行
SELECT * FROM t
WHERE ISNULL(name, '') LIKE '%关键词%'
   OR ISNULL(remark, '') LIKE '%关键词%';

优点:逻辑清晰;

避免任何 UNKNOWN;

兼容所有 SQL Server 版本。

方法 2:显式检查 NULL(更标准)

sql 复制代码
SELECT * FROM t
WHERE (name LIKE '%关键词%' OR name IS NULL)
   OR (remark LIKE '%关键词%' OR remark IS NULL);
-- ❌ 这是错误的!因为 OR name IS NULL 会让所有 NULL 行都返回!

-- ✅ 正确写法(仅当字段非 NULL 时才匹配):
SELECT * FROM t
WHERE (name IS NOT NULL AND name LIKE '%关键词%')
   OR (remark IS NOT NULL AND remark LIKE '%关键词%');

但注意:这和 ISNULL(col, '') LIKE ...效果相同,只是写法不同。

推荐写法(生产环境)

sql 复制代码
-- 方案 A:简洁版(推荐)
WHERE ISNULL(col1, '') LIKE '%关键词%'
   OR ISNULL(col2, '') LIKE '%关键词%'
   OR ISNULL(col3, '') LIKE '%关键词%';

-- 方案 B:显式版(可读性高)
WHERE (col1 LIKE '%关键词%' OR col1 IS NULL) -- ❌ 不要这样写!
-- 正确显式版:
WHERE (col1 IS NOT NULL AND col1 LIKE '%关键词%')
   OR (col2 IS NOT NULL AND col2 LIKE '%关键词%');

强烈推荐方案 A:ISNULL(col, '') LIKE ...代码简短;

性能几乎无损(除非字段极大);

行为符合直觉(NULL 视为"不包含关键词")。

性能提醒

LIKE '%关键词%'无法使用索引,大数据量时很慢;

ISNULL(col, '') 也不会破坏性能(因为本来就不能用索引);

如需高性能全文搜索,考虑 SQL Server Full-Text Search:

sql 复制代码
WHERE CONTAINS((col1, col2), '关键词')

动态 SQL(MyBatis / 应用层)最佳实践

sql 复制代码
<select id="search" resultType="Entity">
  SELECT * FROM t
  WHERE 1=1
  <if test="keyword != null and keyword != ''">
    AND (
      ISNULL(name, '') LIKE CONCAT('%', #{keyword}, '%')
      OR ISNULL(remark, '') LIKE CONCAT('%', #{keyword}, '%')
    )
  </if>
</select>

注意:SQL Server 用 + 拼接字符串,但 MyBatis 推荐用 CONCAT(兼容性好)或参数化。

记住:

NULL LIKE ... 永远不为 TRUE,用 ISNULL 或 COALESCE 将其转为可比较的值是最安全的做法。

相关推荐
qq_349317483 分钟前
mysql如何设置定时自动备份脚本_编写shell脚本与cron任务
jvm·数据库·python
9523612 分钟前
Spring IoC&DI
java·数据库·spring
尚雷558022 分钟前
从电商订单支付更新,吃透 Oracle 数据修改的底层设计哲学与全组件协同原理
数据库·oracle
2401_8323655228 分钟前
Chart.js 4 中基于数据实际范围的线性渐变填充方案
jvm·数据库·python
qq_3422958231 分钟前
如何让 Bootstrap 图标在 Vue 3 中持续旋转动画
jvm·数据库·python
李兆龙的博客35 分钟前
从一到无穷大 #70 从 LR 图 PEC 到InfluxQL兼容性差分测试方法论与工程实践
数据库·功能测试·oracle
爱学习的小囧37 分钟前
ESXi 开启 Secure Boot 后驱动签名验证失败完整处置教程:合规修复与临时测试方案全解
服务器·数据库·esxi·虚拟化
weixin_568996061 小时前
如何用 IndexedDB 存储从 API 获取的超大列表并实现二级索引
jvm·数据库·python
APIshop1 小时前
小红书笔记视频详情接口深度解析:smallredbook.item_get_video_pro
数据库·笔记·音视频
空中海1 小时前
Redis 从零到精通:9大数据结构 × 11个高频工程实战场景完全手册
数据结构·数据库·redis