sql - SQL Server 中的模糊短语相似度

标签 sql sql-server t-sql search fuzzy-search

使用 SQL Server,我应该如何在大型表中的所有行中执行模糊排名搜索,以查找与一列上的长短语的相似性?

换句话说,如果我的数据如下所示:

<表类=“s-表”> <标题> ID 数据 <正文> 1 敏捷的棕色狐狸跳过了懒狗 2 敏捷的棕色猫跳过了懒惰的 Frog 3 懒惰而敏捷的棕色 Frog 跳过了猫 4 lorem ipsum dolor sat amet

我搜索“快速的棕色牛跳过一只懒狗”,我想要的结果大致类似于:

<表类=“s-表”> <标题> ID 得分 <正文> 1 95 2 80 3 40 4 0

实际数据会有更多的行和更长的短语。

显然我不想要精确的字符串匹配,所以使用 LIKECONTAINS显然行不通。

词序很重要,因此单独搜索每个词也是行不通的。

全文索引和声音索引似乎只对子字符串相似性有用,所以我还没有找到将其应用于短语相似性的方法。例如,您如何才能以某种方式查询此内容,从而为缺少或添加单词的类似短语提供不错的分数?

我已经使用编辑距离(Lavenshtein、Jaro-Winkler 等)进行了测试,但对于一大组长字符串来说它太慢了。一个查询需要几分钟的时间。听起来它应该只用于较小的数据,所以我认为这里需要一种不同的方法。

我已经看到提到了 TFIDF 和余弦相似度,但我不确定这是否适合在这里使用,或者它如何在 SQL Server 上实现。

此外,由于我们在 Linux 上使用 SQL Server,因此 CLR 支持受到限制。看起来只要不需要不安全或外部权限就可以。

最佳答案

使用模糊匹配逻辑快速找到最佳匹配字符串的相对快速方法可以基于对字符串中匹配的 3-gram 进行计数。

它可以利用预先构建的 SQL 函数和索引表来加快搜索速度。特别是,它不必检查从搜索字符串到数据集中每个字符串的距离。

首先,为了方便起见,创建一个表函数,将字符串分解为 3 个字母的标记。

drop function dbo.get_triplets;
go
CREATE FUNCTION dbo.get_triplets
(   
    @data varchar(1000)
)
RETURNS TABLE AS RETURN 
(
    WITH Nums AS
    (
      SELECT n = ROW_NUMBER() OVER (ORDER BY [object_id])  FROM sys.all_objects 
    )
    select triplet,count(*) c, len(@data)-2 triplet_count
    from (
        select SUBSTRING(@data,n,3) triplet
        from (select top (len(@data)-2) n from nums) n
    ) triplets
    group by triplet
)
GO

创建字符串数据集

drop table if exists #data;
select * into #data
from (
values
(1, 'the quick brown fox jumps over the lazy dog'),
(2, 'the quick brown cat jumps over the lazy frog'),
(3, 'the lazy quick brown frog jumps over the cat'),
(4, 'lorem ipsum dolor sit amet')
) a(id,data);

创建 3 字母标记的索引表

drop table if exists #triplets;
select id,triplet,c,triplet_count data_triplet_count 
into #triplets
from #data d
cross apply dbo.get_triplets(d.data);

CREATE unique CLUSTERED INDEX IX_triplet_index ON #triplets(triplet,id); 

然后我希望使用类似于的查询对给定字符串的匹配进行有效的模糊搜索

declare @string_to_search varchar(1000) = 'the quick brown ox jumps over a lazy dog';

select  matched.*,d.data,
cast(
cast(matched_triplets as float)
/
cast(case when data_triplet_count>string_triplet_count 
          then data_triplet_count 
          else string_triplet_count
          end as float) 
as decimal(4,3)) score 
from (
    select id,sum(case when a.c<b.c then a.c else b.c end) matched_triplets,
    max(a.data_triplet_count) data_triplet_count,
    max(b.triplet_count) string_triplet_count
    from #triplets a
    join dbo.get_triplets(@string_to_search) b
    on a.triplet = b.triplet
    group by id
) matched
join #data d
on d.id = matched.id;

关于sql - SQL Server 中的模糊短语相似度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73860882/

相关文章:

SQL 通过滑动窗口记录出现次数

sql-server - 跟踪或记录对 SQL Server 中用户定义函数的调用

SQL Server XQuery 修改

c# - 使用 C# 检索 SQL Server 父/子查询结果

java - 在单元测试中捕获 SQLException 而不是 Spring DataIntegrityViolationException

c# 列数据类型日期类型(不是日期时间)

sql-server - 具有-Encoding的Powershell “Import-Csv”在SQL Server中不起作用

sql-server - 无法为数据库 '<temporary system object: 422212632707072>' 中的对象 'tempdb' 分配空间,因为 'PRIMARY' 文件组已满

MYSQL 从表中删除第一个重复项

mysql - 如何根据另一个查询结果进行查询