SQL LIKE语句多条件贪婪匹配算法
在CMS开发中,经常会有类似这样的需求:
提问——回答模式,最经典的例子就是百度提问。
提问者提出问题,由其他人回答,其他人可以是用户,也可以是服务商。
在这个模式中,如何充分利用历史数据是最关键的技术。很多时候,由于客户不擅长使用搜索功能,一上来就提问,而这些问题往往早已经有近乎完美的答案,但没有充分利用。这样一来,不仅加大了劳动量,又增加了数据冗余。
如果在提问的时候能充分调动历史数据,提交问题之前先看看历史问题能不能解决客户疑问,解决了,最好不过,解决不了,再提交。百度提问就是采用的这种方案:

模式固然好,可怎么实现就有些困难了,毕竟这是百度作为搜索引擎的看家本领。
从上图可以看出“CSDN网站如何注册用户”这句话被拆成了N个词,然后分开去数据库中匹配,为什么?因为直接去匹配“CSDN网站如何注册用户”这句话,汉语博大精深,稍微变动一下:“如何在CSDN网站注册用户”,意思完全一样,但直接匹配绝对匹配不到!
因此,我们需要把一句话拆分成词组,这个在网上有现成的组件,比如“庖丁解牛”等,它们大多数是免费开源的。拆成词组之后,应该还要有一个关键词筛选词库,用这个词库确定出有效的词组,比如上图中,“CSDN”、“注册”、“用户”是有效的,而“网站”显然没有匹配,因为它在这句话中没有实际意义。
有点跑题了,拆词、选词不是本文的重点,但却是本文的前提。拿到关键词之后,怎么去数据库中匹配呢?
大家都知道T-SQL中的LIKE语句,通过类似LIKE “%abc%”这样的语法,可以进行模糊匹配,但是它仅仅能进行一次模糊匹配。举个例子:
假如我们确定了a,b,c三个关键词,要查找的记录当然是匹配的越多越好,于是可以这样写:LIKE “%a%b%c%”,这样匹配出的就是包含a,b,c三个关键词的记录,但是如果根本没有包含这三个关键词的记录,最多只有包含两个的,甚至是只包含一个,那么如何写LIKE语句呢?这样LIKE “%a%b%”?这样LIKE “%a%c%”?这样LIKE “%b%c%”?这样LIKE “%a%”?这样LIKE “%b%”?这样LIKE “%c%”?
显然,需要判断的情况太多,简单的LIKE语句已经无法满足需求。需要注意的是,千万不要试图选出范范的记录,返回到程序中去处理,在程序中处理虽然简单,但是范范的记录,在一个中型系统中,往往能达到千万级别,这么大的数据量,从数据库返回到程序,无疑会给服务器造成相当大的压力。
经过探索,本小菜总结了一个比较简单的方法,暂且称为“LIKE语句多条件贪婪匹配算法”。
算法思想:先用LIKE选出每一组符合一个条件的记录,只选择表的主键。然后把这些记录合并在一起,通过主键分组、统计数量,数量最多的,也就是匹配最多的,最后根据数量降序排序,越靠上的记录,匹配的越多。选出匹配的多的记录主键字段,再根据主键去表中选出内容即可。
为了方便大家使用,已经把算法封装成存储过程(直接把下边代码在查询分析器中执行即可)。
存储过程(函数)如下:
GO
CREATE function Get_StrArrayLength
(
@str varchar(1024), --要分割的字符串
@split varchar(10) --分隔符号
)
returns int
as
begin
declare @location int
declare @start int
declare @length int
set @str=ltrim(rtrim(@str))
set @location=charindex(@split,@str)
set @length=1
while @location<>0
begin
set @start=@location+1
set @location=charindex(@split,@str,@start)
set @length=@length+1
end
return @length
end
GO
CREATE function Get_StrArrayStrOfIndex
(
@str varchar(1024), --要分割的字符串
@split varchar(10), --分隔符号
@index int --取第几个元素
)
returns varchar(1024)
as
begin
declare @location int
declare @start int
declare @next int
declare @seed int
set @str=ltrim(rtrim(@str))
set @start=1
set @next=1
set @seed=len(@split)
set @location=charindex(@split,@str)
while @location<>0 and @index>@next
begin
set @start=@location+@seed
set @location=charindex(@split,@str,@start)
set @next=@next+1
end
if @location =0 select @location =len(@str)+1 --这儿存在两种情况:1、字符串不存在分隔符号 2、字符串中存在分隔符号,跳出while循环后,@location为0,那默认为字符串后边有一个分隔符号。
return substring(@str,@start,@location-@start)
end
GO
CREATE PROCEDURE proc_Common_SuperLike
--要查询的表的主键字段名称
@primaryKeyName varchar(999),
--要查询的表名
@talbeName varchar(999),
--要查询的表的字段名称,即内容所在的字段
@contentFieldName varchar(999),
--查询记录的个数(TOP *),匹配的个数越多,排名越靠前
@selectNumber varchar(999),
--匹配字符分隔标记
@splitString varchar(999),
--匹配字符组合字符串
@words varchar(999) AS
declare @sqlFirst varchar(999)
declare @sqlCenter varchar(999)
declare @sqlLast varchar(999)
BEGIN
set @sqlCenter=''
declare @next int
set @next=1
while @next<=dbo.Get_StrArrayLength(@words,@splitString)
begin
--构造sql查询条件(中间部分)
set @sqlCenter = @sqlCenter+'SELECT '+@primaryKeyName+' FROM '+@talbeName+' WHERE '+@contentFieldName+' like ''%'+dbo.Get_StrArrayStrOfIndex(@words,@splitString,@next)+'%'' UNION ALL '
set @next=@next+1
end
--处理sql语句中间部分,去除最后无用语句
set @sqlCenter=left(@sqlCenter,(len(@sqlCenter)-10))
--构造sql语句开头部分
set @sqlFirst='SELECT TOP '+@selectNumber+' '+@primaryKeyName+',COUNT(*) AS showCout FROM ('
--构造sql语句结尾部分
set @sqlLast=') AS t_Temp GROUP BY '+@primaryKeyName+' ORDER BY showCout DESC'
--拼接出完整sql语句,并执行
execute(@sqlFirst+@sqlCenter+@sqlLast)
END
调用示例:
- executeproc_Common_SuperLike 'id','t_test','content','20','|','i|o|c'
id表的主键字段名称。
t_test表名。
content匹配内容字段名称。
20选出20个记录(从顶至下匹配度越来越低)。
|关键字的分隔符号。
i|o|c一共有i,o,c三个关键字,通过|分隔。
SQL LIKE语句多条件贪婪匹配算法的更多相关文章
- SQL 视图 局部变量 全局变量 条件语句 事务 触发器
一.视图 1.视图是一张虚拟表,他所存储的不是实际数据,而是查询语句,但我们可以对视图进行像数据表一样的操作. 2.为什么使用视图呢?我的理解是:1.在远程传输数据时,可以避免过长的查询字符,减少流量 ...
- sql语句之条件,分页,排序
sql语句之条件,分页,排序
- SQL查询语句分类
SQL查询语句有多种,下面总结下.首先先建三张表用于后面的实验 -- 学生表,记录学生信息 CREATE TABLE student( sno ), sname ), ssex ENUM('男','女 ...
- [转]MySQL 最基本的SQL语法/语句
MySQL 最基本的SQL语法/语句,使用mysql的朋友可以参考下. DDL-数据定义语言(Create,Alter,Drop,DECLARE) DML-数据操纵语言(Select,Delete ...
- SQL入门语句之ORDER BY 和GROUP BY
一.SQL入门语句之ORDER BY ORDER BY 是用来基于一个或多个列按升序或降序顺序排列数据 1.从数据库表获取全部数据按字段A的升序排列 select *from table_name o ...
- SQL入门语句之LIKE、GLOB和LIMIT
一.SQL入门语句之LIKE LIKE用来匹配通配符指定模式的文本值.如果搜索表达式与模式表达式匹配,LIKE 运算符将返回真(true),也就是 1.这里有两个通配符与 LIKE 运算符一起使用,百 ...
- SQL入门语句之SELECT和WHERE
一.SQL入门语句之SELECT SELECT语句用于从数据库表中获取数据,结果表的形式返回数据.这些结果表也被称为结果集 1.从数据库表中取部分字段 select 字段A,字段B from tabl ...
- SQL入门语句之INSERT、UPDATE和DELETE
一.SQL入门语句之INSERT insert语句的功能是向数据库的某个表中插入一个新的数据行 1.根据对应的字段插入相对应的值 insert into table_name(字段A, 字段B, 字段 ...
- SQL SELECT 语句
本章讲解 SELECT 和 SELECT * 语句. SQL SELECT 语句 SELECT 语句用于从表中选取数据. 结果被存储在一个结果表中(称为结果集). SQL SELECT 语法 SE ...
随机推荐
- 有趣的win8进度条
有趣的win8进度条 刚才在安装visual studio 12,发现它的安装界面都是win8风格的,而且安装的时候有个进度条,看着挺不错,就用 jquery 实现了一下,的确挺有趣: 点击停止效果 ...
- 【Win32API】SendInput ERROR_BUSY 错误原因
最近需要解决一个Windows上模拟键盘输入的问题, 使用SendInput这个API来实现的.当我从另外一台机器给当前机器发送一条键盘指令时,发现SendInput一直是成功的,但是没有看到任何输入 ...
- linux下安装NPM管理工具
根据”挖一下“开发需要,选择nodejs实现异步IO,目的是为了解决服务器卡死导致无法处理后续的http请求.看了花瓣的架构视频讲座,才决定这么做的,挺有道理的. 安装nodejs很顺利,下载源码包, ...
- UITableView的常用方法与示例
实例方法 dequeueReusableCellWithIdentifier: 初始化一个指定重用标识符的UITableCell对象 两个协议 UITableViewDataSource tableV ...
- Jenkins+PMD构建自动化静态代码检测
前言:软件缺陷是不可避免的,要尽量减少错误并提高软件质量,主要有两在类技术,即缺陷预防和缺陷检测 缺陷预防包括编写更好的设计规范.实施代码审核制度.运行代码静态分析工具.运行单元测试等 PMD是一种开 ...
- 12157 - Tariff Plan
Ampang Communications & Mobile (ACM) provides telecom services for various types of users. Sin ...
- 从ICassFactory为CLSID为{17BCA6E8-A950-497E-B2F9-AF6AA475916F}的COM组件创建实例失败,原因是出现以下错误:c001f011.(Microsoft.Server.manageDTS
从ICassFactory为CLSID为{17BCA6E8-A950-497E-B2F9-AF6AA475916F}的COM组件创建实例失败,原因是出现以下错误:c001f011.(Microsoft ...
- gRPC helloworld service, RESTful JSON API gateway and swagger UI
概述 本篇博文完整讲述了如果通过 protocol buffers 定义并启动一个 gRPC 服务,然后在 gRPC 服务上提供一个 RESTful JSON API 的反向代理 gateway,最后 ...
- C语言之循环结构 for(一)
一 for循环的介绍 语法: for(表达式1;表达式2;表达式3){ 循环体; } 循环步骤: A.执行表达式1,执行完毕跳转到B B.判断表达式2的结果是否为真,如果为真,跳转到C,否则跳转到E ...
- ASP.NET Zero--6.菜单加权限
1.打开文件MpaNavigationProvider.cs [..\MyCompanyName.AbpZeroTemplate.Web\Areas\Mpa\Startup\MpaNavigation ...