引言
今天在SQL Server技术群里,有一位群友问到这样一个问题:表中某列数据如下图,希望能够提取值为7的记录。几经沟通之后发现,真实的需求是:希望用 SQL 将一个字段内用逗号分隔的内容分成多条记录,然后取值为7的记录。
有时候我们在设置数据库的时候,基于查询和业务的考虑会将数据以符号分隔的方式存储在一个字段内。这样的设计节省了空间,也少了一张扩展的关系表,但是却为查询带来了麻烦。关系型数据库对于这样的查询,能力略显不足。但也不是无技可施,众多的数据库大神们早已分享出了许多经典的将逗号分隔内容拆分多列的代码。在此小弟先膜拜过。
今天要分享的并非这些经典案例,而是如何在不拆分字符串内容的情况实现查询匹配。当然,仅限于单条件的查询匹配,如果是需要和字典表关联或者是多条件查询,仍要膜拜那些经典案例了。此外,在数据量大的情况下也不建议使用,以实际测试为准。
知识点
charindex
函数返回字符或者字符串在另一个字符串中的起始位置(索引从1开始)。CHARINDEX ( expression1 , expression2 [ , start_location ] )
patindex
函数返回字符或者字符串在另一个字符串或者表达式中的起始位置,PATINDEX函数支持搜索字符串中使用通配符,这使PATINDEX函数对于变化的搜索字符串很有价值(索引从1开始)。PATINDEX ( '%pattern%' , expression )。
SQL通配符
单组字符串匹配
declare @search varchar(10)
set @search = '7'
;
with tmp as
(
select 1 id, '873,874' gd_qxry
union all
select 2, '874,7,873'
union all
select 3, '7,832'
union all
select 4, '72,83,7'
)
select *
from tmp
where charindex(','+@search+',',','+gd_qxry+',')>0
有两个大家很容易想到,但是不完善的实现
a> charindex(@search,gd_qxry)>0,此实现没有考虑到如'873'这样,分隔内容包含7的情况。
b> charindex(','+@search+',',gd_qxry)>0,此实现没有考虑到如'7,873'、'873,7',7在分隔内容最前或最后的情况。
多组字符串匹配
接着这位群友又提出了,以上方案仅能实现一组字符串的匹配。假设我现在一个字段存储多组字符串,该怎么办呢。如:874,6,873|7,873,72。其实我们分析一下即可明白,所谓分组不过是又多出来一个分隔符而已,但是对于charindex来说已经是致命的了。不过,这时候得膜拜下微软的研发团队了,即生亮,又生瑜,Parindex出现了。
declare @search varchar(10)
set @search = '7'
;
with tmp as
(
select 1 id, '874,6,873|7,873,72' gd_qxry
union all
select 2, '874,7,873|832,73'
union all
select 3, '71,832|83,77'
union all
select 4, '72,83,7|872,33'
)
select *
from tmp
where patindex('%[,|]'+@search+'[,|]%',','+gd_qxry+',')>0
Patindex的妙处在于支持通配符。我一度的想,要是支持正则表达式该有多好。不过SQL也不是不能,CLR都支持了还有什么是不能的。
PS
以上仅是本人臆想的测试数据,欢迎各位看官提出异常情况批评指正,在此叩谢。