【Oracle】SQL字符串查找_精确查询,只要23不要234

今日用比较优雅的方法解决了一个挺恼人的问题,特记录之。

1 背景

学生表 studeng 有字段:

id, name, courseIDs

分别对应 

ID,学生姓名,学生选择的课程ID串

23 |  张三 |  ‘234, 235’

24 |  李四 |  '11,12,13'

课程表 course 有字段:

id, name, score

分别对应

ID,课程名称,课程成绩

23   | 大学语文 | 99

234   | 大学物理 | 100

235   | 大学英语 | 120

上面有2个表。学生表和课程表。

学生表的 courseIDs 里面保存着该学生选择的课程的 ID拼接出的字符串。这是比较常见的设计方式。

2 问题

存取都比较方便方便,但是有个问题。比如我们要

查找

所有选择

大学语文

的学生姓名

该如何写SQL语句呢?

3 探索

我在下面的 SQL 语句中,直接使用大学语文的课程 id,即 23 代表大学语文。

通过“大学语文”文字查找其课程 id 不是本文的重要内容。

首先,通过逻辑推算,我们知道问题中的查找结果应为 空结果集

即,张三和李四都没有选择 大学语文课。

3.1 方法一,初级写法,使用 like 关键词

like 是 SQL 语法中的关键词,也是最常用的模糊查询方法。

select s.name 
from student s 
where 
s.courseIDs like '%23%'

查询结果:

张三

这明显是错误的,但是为什么会发生呢?

因为 like '%%' 是模糊查询,只要匹配就返回。

234, 235’ 是满足 like '%23%' 的要求的,因为里面有 23 。

这是很严重,也经常容易出现的错误。

3.2 方法二,使用 instr() 、substr() 函数

注意:instr() 、substr() 都是Oracle数据库软件的内置函数,并非标准的 SQL 语法关键词。也就是说用到这些函数的SQL语句,在MySQL和MS SQL Server是直接报语法错误、无法执行的,只能在 Oracle 数据库中使用。

题外话:其实在 MySQL 中,使用MySQL的内置函数 find_in_set 实现这个问题就特别简单,后面会讲。

这些函数的具体解释不多说了,简单说就是 instr() 返回目标字符串在源头字符串的位置,若查无结果,则返回0。例如:

select instr('helloworld','lo') from dual;  返回结果:4 即:在“lo”中,“l”开始出现的位置。

用这个函数写出SQL语句:

select s.name 
from student s 
where 
instr(s.courseIDs, '23') != 0

查询结果:

张三

同样是错误的。其实 instr() 函数 和 like 关键词有些类似,都是只要出现 23 就认为匹配成功了。而我们要的是独立的23 ,而不是 234 里面的 23。

3.3 方法三,高阶玩法,正则表达式函数 regexp()

 很容易的,我们就想到 正则表达式 中的贪婪查询。

正则表达式 就是字符串匹配和查找的神,基本可以满足所有的查找和替换需求。

如果你是一名程序员,却不了解正则表达式,那你一定是一名不合格的程序员。

Oracle内置有四个正则表达式查询函数,非标准SQL语法。 

REGEXP_LIKE() :与LIKE() 的功能相似
REGEXP_INSTR() :与INSTR() 的功能相似
REGEXP_SUBSTR() :与SUBSTR() 的功能相似
REGEXP_REPLACE() :与REPLACE() 的功能相似

正则表达式用法我就不写了,反正就是很复杂。下面直接讲我的简单解决方案。

3.4 博主实现的简单方案

思路

其实我们需求很明确,就是只要 23 不要 234。就是如何精确的查找到 独立的 23。

我们观察 courseIDs 用逗号 “,”拼接出的字符串,这个逗号“,"本身就是个天然的分隔符,既然本身就有分隔符,为什么查询的时候不可以用呢?

阻碍1

可以使用  23,  来查询,但是 couseIDs 字符串  234,235  最后末尾少了个逗号啊。这个表的数据我没有权限修改,不能在数据后面加逗号。

并且如果大学语文是 34,那我用   34,   来搜索  234,235  ,依然是查找成功,即查询结果依然会返回张三,说明这个查询策略是有严重BUG的。

阻碍2

那我前面加个逗号,使用 ,23, 来查询呢。但是  couseIDs 字符串 234,235   不仅末尾少了个逗号,最前面也没有逗号,碰到查询第一个和最后一个ID就会查询不到。我们查询  ,234,  就会遗漏了张三。怎么办呢?

解决

我们无需修改数据库表中的数据,只需要在SQL语句中在字段内容前后 拼接上一前一后两个逗号就行了。

查询得知,Oracle有个运算符 “||”可以拼接字符串。于是,有下面的SQL:

select s.name 
from student s
where 
instr(
','||s.courseIDs||',' 
,
',23,') != 0

原理很简单:在 s.courseIDs 即  234,235  的首尾各加一个 逗号(变成 ,234,235, ),然后再使用 instr() 函数查询该字符串   ,234,235,  里面是否有 ,23,  。这样就可以使用天然的逗号分隔符,把每个独立的ID数字都独立的包裹起来。

限制条件

很明显,这个方法必须在该字段的数据非常规整的前提下使用的。如果数字与逗号之间出现空格或空白字符,会查询失败。如  ,56   ,  88,   可能就无法查找到  ,56,   ,88,   

<全文完>

写文不易,就这一行语句写了近三个小时。如果有收获请点赞支持。

全博所有文章欢迎转载,转载注明出处即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qilei2010

送我一张彩票中了平分

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值