SQL Server字符串左匹配

在SQL Server中经常会用到模糊匹配字符串的情况,最简单的办法就是使用like关键字(like语法http://msdn.microsoft.com/en-us/library/ms179859.aspx)。但是如果我们使用的前后都加%的方式,是没办法用到索引进行快速查询的,所以很多情况下我们使用左匹配的方式。最常见的一个例子就是在搜索框中,用户输入了一部分关键字,系统可以通过用户的输入进行左匹配,找出相关的结果列出来。使用左匹配的好处是可以使用到SQL Server中对该字段建立的索引,使得查询效率很高,但是不好的SQL语句仍然会导致索引无法使用。

假设我们现在有个表YCMRSALE,其中有个字段MATNR存储了料号信息,如果我们要从这个表中查询出以AB开头的料号,如果使用NHibernate,那么我们常用的写法有:

//QueryOver的写法
var result = session.QueryOver<Ycmrsale>().WhereRestrictionOn(c => c.Matnr).IsLike("AB", MatchMode.Start).List<Ycmrsale>();
//Linq to NHibernate
result = session.Query<Ycmrsale>().Where(c => c.Matnr.StartsWith("AB")).ToList();
//Criteria写法
result = session.CreateCriteria<Ycmrsale>().Add(Expression.Like("Matnr", "AB", MatchMode.Start)).List<Ycmrsale>();

这几种写法本质上都是生成了如下的where条件语句:

where ycmrsale0_.Matnr like 'AB%'

如果使用EntityFramework,那么查询的C#代码也和NHibernate类似:

var result = bwEntities.YCMRSALEs.Select(s => s.MATNR).Where(s => s.StartsWith("AB"));

where条件也是一样的:

WHERE [Extent1].[MATNR] LIKE 'AB%'

这里只是举了最简单的情况,如果我们要查询的料号本身就包含%,比如要查询以”%00”开头的料号,那么怎么保证这里的%是百分号而不是表示模糊匹配的意思呢?

使用EntityFramework就很简单,什么都不需要修改,系统会根据传入的字符串生成不同的SQL语句:

var result = bwEntities.YCMRSALEs.Select(s => s.MATNR).Where(s => s.StartsWith("%00"));

生成的SQL Where条件:

WHERE [Extent1].[MATNR] LIKE '~%00%' ESCAPE '~'

对开发人员来说,真是很简单,什么输入都不用管。但是如果用NHibernate就要麻烦点了,我们必须要判断用户输入的字符串里面是否有特殊转移符,如果有,那么就需要进行替换,而且C#查询语句也有所不同。

string input = "%00";
Regex regex=new Regex(@"[~%\[\]_]");
input= regex.Replace(input, delegate(Match m) { return "~" + m.Value; });
var result = session.QueryOver<Ycmrsale>().WhereRestrictionOn(c => c.Matnr).IsLike(input, MatchMode.Start,'~').List<Ycmrsale>();
生成的SQL Where条件:
WHERE this_.Matnr like @p0 escape '~';@p0 = '~%00%'

以上说的都是在ORMapping的工具中进行左匹配查询,如果我们要在SQL语句中直接进行查询还有一种写法就是用left函数。同样以YCMRSALE表举例,如果我们有另一表matnr,该表中的matnr列存储了不完整的料号,现在需要将两个表join起来,使用matnr列进行左匹配,那么我们的SQL可以写成:

select *
from YCMRSALE s
inner join matnr m
on left(s.MATNR,len(m.matnr))=m.matnr

这个写法能够得到我们想要的结果,但是由于对MATNR列使用了函数,所以无法使用索引,所以查询速度很慢。

如果我们要改写成like的形式,那么就需要对matnr表中的matnr列进行处理,将特殊字符进行替换,将~%_[]这几个字符都替换掉。所以我们的SQL查询就会变成这样:

select *
from YCMRSALE s
inner join matnr m
on s.MATNR like replace(replace(replace(replace( replace(m.matnr,'~','~~'),'_','~_'),'[','~['),']','~]'),'%','~%')+'%' escape '~'

这里的SQL虽然看起来比较Ugly,但是却可以用上YCMRSALE表上对MATNR建立的索引,所以效率较高。

除了ESCAPE这个关键字的处理方式外,微软官方还给出了另一种解决办法,那就是使用“[]”将转义字符括起来。这种写法比escape关键字的写法要简单点,对应的SQL为:

select *
from YCMRSALE s
inner join matnr m
on s.MATNR like replace(replace(replace( m.matnr,'[','[[]'),'_','[_]'),'%','[%]')+'%'

甚至我们还可以先写个自定义函数对转移字符进行处理对于join的情况,那就非常复杂了。。。

create function OpStr(@input varchar(50))
returns varchar(100)
as begin declare @i int=1;
declare @result varchar(100)='';
declare @c char(1)
while(@i<=len(@input))
begin set @c=substring(@input,@i,1);
 if (@c='[' or @c='%' or @c='_')
 begin set @result+='['+@c+']';
 end else begin set @result+=@c;
 end set @i+=1;
end return @result end
然后在查询中调用这个自定义的函数即可。
select *
from YCMRSALE s
inner join matnr m
on s.MATNR like dbo.OpStr(m.matnr)+'%'
时间: 2024-09-10 12:40:26

SQL Server字符串左匹配的相关文章

SQL Server字符串拼接

问题描述 SQL Server字符串拼接 正常顺序:①②③④⑤⑥⑦⑧⑨⑩??W1W2↑这个是正常的现实顺序现在有 '⑥⑦⑧⑩??W1W2' 这么一个字符串 也可能是 'W1W2'也可能是 '①②③'我怎么把 ⑨放到这个正常顺序里面去求一个FUN()

SQL Server 字符串处理函数

SQL Server 字符串处理函数 ASCII:返回字符表达式中最左侧的字符的 ASCII 代码值. select ASCII(expression) CHAR:将 int ASCII 代码转换为字符. CHAR 可用于将控制字符插入字符串中 制表符 char(9) 换行符 char(10) 回车符 char(13) CHARINDEX:在 expression2 中搜索 expression1 并返回其起始位置(如果找到).搜索的起始位置为 start_location.如果 express

SQL SERVER字符串函数

本篇文章还是学习<程序员的SQL金典>内容的记录,此次将讲解的是SQL SERVER的字符串函数. 其实数据库跟程序语言库一样,都会集成很多可以使用的API.这些API,如果你熟悉的话,将减少在 代码层次的再次加工操作. 我想字符串函数的使用价值还是很高的,所以我觉得一边学习一起把常用的东西都记录下来.一来加 深自己的悟性,二来通过归纳使知识为我所用. SQL SERVER支持的字符串函数(其它数据库这里就不罗列了,想看更多的可以关注<程序员的SQL金 典>),本文将介绍如下内容:

SQL Server字符串区别大小写方法

在SQL Server中默认对大小写是不敏感的,例如userName='jesse'和userName='JESSE'结果是一样的.在验证密码的时候可能就需要对字符串大小写敏感,需要做一些处理,介绍两种方法: 法Ⅰ:转换成二进制再比较,由于大小写的ASC码不同.例如: select * from T_User where cast(field as varbinary) = cast( 'Admin' as varbinary) 法Ⅱ:利用排序规则,也是基于二进制.在字段后加上collate C

sql server字符串非空判断实现方法_MsSql

复制代码 代码如下: CREATE function getcolor (@井号 varchar(50),@层位 varchar(50)) returns varchar(200) as begin declare @sql varchar(2000);set @sql='' --distinct select @sql=@sql+ 颜色+',' from ( select distinct 颜色 from yourtablename where 井号=@井号 and 层位=@层位 and 颜色

sql server字符串非空判断实现方法

复制代码 代码如下: CREATE function getcolor (@井号 varchar(50),@层位 varchar(50)) returns varchar(200) as begin declare @sql varchar(2000);set @sql='' --distinct select @sql=@sql+ 颜色+',' from ( select distinct 颜色 from yourtablename where 井号=@井号 and 层位=@层位 and 颜色

Sql Server 字符串聚合函数_MsSql

如下表:AggregationTable Id Name 1 赵 2 钱 1 孙 1 李 2 周 如果想得到下图的聚合结果 Id Name 1 赵孙李 2 钱周 利用SUM.AVG.COUNT.COUNT(*).MAX 和 MIN是无法做到的.因为这些都是对数值的聚合.不过我们可以通过自定义函数的方式来解决这个问题.1.首先建立测试表,并插入测试数据: 复制代码 代码如下: create table AggregationTable(Id int, [Name] varchar(10)) go

SQL Server 字符串处理

DECLARE @str VARCHAR(50) SET @str='AP-FQC-2014072300004' --获取指定字符第一次出现的位置 SELECT PATINDEX('%-%',@str) --返回:3 --获取指定字符第一次出现的位置之前的字符串 SELECT SUBSTRING(@str,1,CHARINDEX('-',@str)-1) --返回:AP --获取指定字符最后一次出现的位置 SELECT LEN(@str)-CHARINDEX('-',REVERSE(@str))

Sql Server 字符串聚合函数

如下表:AggregationTable Id Name 1 赵 2 钱 1 孙 1 李 2 周 如果想得到下图的聚合结果 Id Name 1 赵孙李 2 钱周 利用SUM.AVG.COUNT.COUNT(*).MAX 和 MIN是无法做到的.因为这些都是对数值的聚合.不过我们可以通过自定义函数的方式来解决这个问题.1.首先建立测试表,并插入测试数据: 复制代码 代码如下:create table AggregationTable(Id int, [Name] varchar(10)) go i