sql 数据库可以返回一个 sql 结果集加上结果的分数吗?

Posted

技术标签:

【中文标题】sql 数据库可以返回一个 sql 结果集加上结果的分数吗?【英文标题】:Could a sql database return a sql result set plus a score for the results? 【发布时间】:2011-01-31 23:44:03 【问题描述】:

只是好奇,如果我想将字符串发送到数据库(可能是 MS SQL Server),任何人都可以提供有关从数据库返回结果的最佳方法的任何见解,其中结果集可能被排序和“评分” " 与传入的字符串是否接近?

所以,如果我发送查询:

SELECT name FROM table where name LIKE 'Jon'

然后得到 1000 个结果的结果,如下所示:

100 Jon
98  John
80  Jonathan
32  Nathan

视图、索引、存储过程、编码解决方案?有什么建议?

【问题讨论】:

en.wikipedia.org/wiki/Levenshtein_distance 为什么“Nathan”会出现在结果中? @Mitch - 你如何应用它,因为 Levenshtein + LIKE 总是只是 Len(str) - Len(like-expr)... @cyberkiwi:你读过维基页面吗? @Mitch - 如果 2 个字符串类似于 '%JON%',则 JON 已经是两个字符串的一部分,因此 Levenshtein 距离 = 要添加的字符数 = len(A)-len(B ) 对?我确实扫描了 wiki,但什么也没看到 【参考方案1】:

你可以,但你需要使用另一个函数来做到这一点。 Levenshtein ratio 或Jaro distance 将是最常见的解决方案。我不确定 SQL Server 包含什么(如果有的话)为此内置的。如果不出意外,我认为您可以使用 here 所述的 SimMetrics 库。无论如何,它看起来像这样。

select top 1000
jaro('John', name) as score, name
from table
where name like '%John%'
order by 1 desc

【讨论】:

对于在这个网站上积分很少的人来说,这是一个很好的答案。谢谢。另外,感谢cyberkiwi。【参考方案2】:

编辑

由于来自 cmets 的一些持续刺激,我在这里展示了一个用 SQL 计算 Levenshtein 距离的实现。此处使用 SQL Server 2005+ 的 TSQL,但该技术也可以转换为其他 DBMS。 最高分为 100

;with tbl as (
    select 'Jon' AS Name union all
    select 'Jonathan' union all
    select 'Jonny' union all
    select 'John' union all
    select 'Bone' union all
    select 'BJon' union all
    select 'Nathan' union all
    select 'Jonne')
SELECT *, SCORE_Levenshtein + SCORE_SOUNDEX TotalScore
FROM
(
SELECT name,
    CAST(50 /
    (
        select 1.0 + MAX(LDist)
        FROM
        (
            select startAt.number,
                LEN(longer) -
                sum(case when SUBSTRING(longer, startAt.number+offset.number, 1)
                            = SUBSTRING(shorter, 1+offset.number, 1) then 1 else 0 end ) LDist
            FROM
            (select case when LEN(Name) < LEN(LookFor) then Name else LookFor end shorter) shorter
            cross join
            (select case when LEN(Name) >= LEN(LookFor) then Name else LookFor end longer) longer
            inner join master..spt_values startAt
                on startAt.type='P' and startAt.number between 1 and len(longer) - LEN(shorter) + 1
            inner join master..spt_values offset
                on offset.type='P' and offset.number between 0 and LEN(shorter)-1
            group by startAt.number, longer, shorter
        ) X
    ) AS NUMERIC(16,4)) SCORE_Levenshtein
    ,
    CAST(50 / (5-  -- inversely proportional to soundex difference
    (
    SELECT 0.0 +
    case when Substring(A,1,1)=Substring(B,1,1) then 1 else 0 end
    +
    case when Substring(A,2,1)=Substring(B,2,1) then 1 else 0 end
    +
    case when Substring(A,3,1)=Substring(B,3,1) then 1 else 0 end
    +
    case when Substring(A,4,1)=Substring(B,4,1) then 1 else 0 end
    FROM (select soundex(name) as A, SOUNDEX(LookFor) as B) X
    )) AS NUMERIC(16,4)) AS SCORE_SOUNDEX
FROM tbl
CROSS JOIN (SELECT 'Jon' as LookFor) LookFor
) Scored
Order by SCORE_Levenshtein + SCORE_SOUNDEX DESC

注意 - 使用此行 CROSS JOIN (SELECT 'Jon' as LookFor) LookFor 以便输入 'Jon' 不需要在查询中重复多次。也可以定义一个变量,并在查询中使用LookFor 的地方使用它。

输出

值得注意的是,Jonny 与 SOUNDEX 一起获得了高于 Bone 的分数,而单独使用 Levenshtein 不会发生这种情况。

name      SCORE_Levenshtein  SCORE_SOUNDEX   TotalScore
Jon       50.0000            50.0000         100.0000
John      12.5000            50.0000          62.5000
Jonny      8.3333            50.0000          58.3333
Jonne      8.3333            50.0000          58.3333
Bone      10.0000            25.0000          35.0000
BJon      10.0000            12.5000          22.5000
Jonathan   5.5556            16.6667          22.2223
Nathan     7.1429            12.5000          19.6429


原始答案如下,基于基于 LIKE '%x%' 的预过滤输入,它将 Levenshtein 折叠为简单的 Len(column) - Len(Like-expression) 计算

看看这个例子 - 它测试了长度和 SOUNDEX 的差异,因为没有更好的度量。

最高分是 100。

;with tbl as (
    select 'Jon' AS Name union all
    select 'Jonathan' union all
    select 'Jonny' union all
    select 'John' union all  -- doesn't match LIKE
    select 'BJon' union all
    select 'Jonne')
SELECT name,
     50 / (Len(Name) - LEN('Jon') + 1.0)  -- inversely proportional to length difference
     +
     50 / (5-  -- inversely proportional to soundex difference
    (
    SELECT 0.0 +
    case when Substring(A,1,1)=Substring(B,1,1) then 1 else 0 end
    +
    case when Substring(A,2,1)=Substring(B,2,1) then 1 else 0 end
    +
    case when Substring(A,3,1)=Substring(B,3,1) then 1 else 0 end
    +
    case when Substring(A,4,1)=Substring(B,4,1) then 1 else 0 end
    FROM (select soundex(name) as A, SOUNDEX('Jon') as B) X
    )) AS SCORE
FROM tbl
where name LIKE '%Jon%'
Order by SCORE DESC

输出

name     SCORE
Jon      100.00000000000000000
Jonny    66.66666666666660000
Jonne    66.66666666666660000
BJon     37.50000000000000000
Jonathan 24.99999999999996666

【讨论】:

我喜欢你批评 Levenstein 距离的适用性,然后提出糟糕的 SOUNDEX 实现的方式! ***.com/questions/42013/… @Mitch - 让我们看看你的建设性回答【参考方案3】:

这样的事情可能会有所帮助:

http://www.mombu.com/microsoft/microsoft/t-equivalent-sql-server-functions-for-match-against-in-mysql-2292412.html

【讨论】:

以上是关于sql 数据库可以返回一个 sql 结果集加上结果的分数吗?的主要内容,如果未能解决你的问题,请参考以下文章

oracle 存储过程执行动态SQL 返回结果给游标,外部程序获得dataset结果集。

SSIS Execute SQL Task 用法

Hibernat 原生SQL运行结果集处理方法

SQL - 如果结果集包含多于一行,则返回最新(id)结果

如何将单个结果集从返回多个集的 SQL 存储过程保存到临时表?

ORA-06504: PL/SQL: 执行时返回结果集变量的类型