SQL Server 查找包含相似字符串的sql记录

fivyi3re  于 2022-12-17  发布在  其他
关注(0)|答案(7)|浏览(216)

我有下表2列:包含超过500,000条记录的ID和标题。例如:

ID  Title
--  ------------------------
1   Aliens
2   Aliens (1986)
3   Aliens vs Predator
4   Aliens 2
5   The making of "Aliens"

我需要找到非常相似的记录,我的意思是它们相差3-6个字母,通常这种差异在标题的末尾。所以我必须设计一个查询,返回记录1、2和4。我已经看过levenstein distance,但我不知道如何应用它。另外,由于记录的数量,查询不应该花一整夜。
谢谢你的意见或建议

eagi6jfj

eagi6jfj1#

如果你真的想用你在问题中表述的方式来定义相似性,那么你就必须实现Levensthein Distance计算,要么在代码中对DataReader检索到的每一行进行计算,要么作为SQL Server函数。
这个问题实际上比乍看之下要复杂得多,因为你不能假定知道两个字符串之间的"共有“元素可能是什么。
因此,除了Levensthein Distance之外,您可能还需要指定实际必须匹配的连续字符的最小数量(以便得出足够的相似性)。
总之:这听起来像是一个过于复杂和耗时/缓慢的方法。
有趣的是,在SQL Server 2008中,您可以使用DIFFERENCE函数来执行类似的操作。
它评估两个字符串的语音值并计算差值。我不确定你是否能让它在多词表达式(如电影标题)中正常工作,因为它不能很好地处理空格或数字,并且过于强调字符串的开头,但它仍然是一个值得注意的有趣 predicate 。
如果您 * 实际上 * 试图描述的是某种搜索功能,那么您应该研究一下SQL Server 2008的Full Text Search功能。它提供了内置的Thesaurus support、花哨的SQL predicates和“最佳匹配”排名机制
编辑:如果您正在寻找消除重复,也许您可以看看SSIS Fuzzy Lookup and Fuzzy Group Transformation。我自己没有尝试过这个,但它看起来是一个有希望的线索。
编辑2:如果您不想深入研究SSIS,并且仍然与Levensthein Distance算法的性能作斗争,那么您也许可以尝试一下algorithm,它看起来不那么复杂。

zkure5ic

zkure5ic2#

对于所有遇到这个问题的谷歌人,虽然它已经被标记为已回答,但我想我应该分享一些代码来帮助解决这个问题。如果你能够在SQL Server上执行CLR用户定义函数,你可以实现自己的Levensthein Distance算法,然后从那里创建一个函数,给你一个名为dbo.GetSimilarityScore()的“相似性得分”。我的得分是基于大小写不敏感的。没有太多的权重混乱的词序和非字母数字字符。你可以调整你的评分算法需要,但这是一个很好的开始。信贷this code project link让我开始。

Option Explicit On
Option Strict On
Option Compare Binary
Option Infer On

Imports System
Imports System.Collections.Generic
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.SqlTypes
Imports System.Text
Imports System.Text.RegularExpressions
Imports Microsoft.SqlServer.Server

Partial Public Class UserDefinedFunctions

    Private Const Xms As RegexOptions = RegexOptions.IgnorePatternWhitespace Or RegexOptions.Multiline Or RegexOptions.Singleline
    Private Const Xmsi As RegexOptions = Xms Or RegexOptions.IgnoreCase

    ''' <summary>
    ''' Compute the distance between two strings.
    ''' </summary>
    ''' <param name="s1">The first of the two strings.</param>
    ''' <param name="s2">The second of the two strings.</param>
    ''' <returns>The Levenshtein cost.</returns>
    <Microsoft.SqlServer.Server.SqlFunction()> _
    Public Shared Function ComputeLevenstheinDistance(ByVal string1 As SqlString, ByVal string2 As SqlString) As SqlInt32
        If string1.IsNull OrElse string2.IsNull Then Return SqlInt32.Null
        Dim s1 As String = string1.Value
        Dim s2 As String = string2.Value

        Dim n As Integer = s1.Length
        Dim m As Integer = s2.Length
        Dim d As Integer(,) = New Integer(n, m) {}

        ' Step 1
        If n = 0 Then Return m
        If m = 0 Then Return n

        ' Step 2
        For i As Integer = 0 To n
            d(i, 0) = i
        Next

        For j As Integer = 0 To m
            d(0, j) = j
        Next

        ' Step 3
        For i As Integer = 1 To n
            'Step 4
            For j As Integer = 1 To m
                ' Step 5
                Dim cost As Integer = If((s2(j - 1) = s1(i - 1)), 0, 1)

                ' Step 6
                d(i, j) = Math.Min(Math.Min(d(i - 1, j) + 1, d(i, j - 1) + 1), d(i - 1, j - 1) + cost)
            Next
        Next
        ' Step 7
        Return d(n, m)
    End Function

    ''' <summary>
    ''' Returns a score between 0.0-1.0 indicating how closely two strings match.  1.0 is a 100%
    ''' T-SQL equality match, and the score goes down from there towards 0.0 for less similar strings.
    ''' </summary>
    <Microsoft.SqlServer.Server.SqlFunction()> _
    Public Shared Function GetSimilarityScore(string1 As SqlString, string2 As SqlString) As SqlDouble
        If string1.IsNull OrElse string2.IsNull Then Return SqlInt32.Null

        Dim s1 As String = string1.Value.ToUpper().TrimEnd(" "c)
        Dim s2 As String = string2.Value.ToUpper().TrimEnd(" "c)
        If s1 = s2 Then Return 1.0F ' At this point, T-SQL would consider them the same, so I will too

        Dim score1 As SqlDouble = InternalGetSimilarityScore(s1, s2)
        If score1.IsNull Then Return SqlDouble.Null

        Dim mod1 As String = GetSimilarityString(s1)
        Dim mod2 As String = GetSimilarityString(s2)
        Dim score2 As SqlDouble = InternalGetSimilarityScore(mod1, mod2)
        If score2.IsNull Then Return SqlDouble.Null

        If score1 = 1.0F AndAlso score2 = 1.0F Then Return 1.0F
        If score1 = 0.0F AndAlso score2 = 0.0F Then Return 0.0F
        ' Return weighted result
        Return (score1 * 0.2F) + (score2 * 0.8F)
    End Function

    Private Shared Function InternalGetSimilarityScore(s1 As String, s2 As String) As SqlDouble
        Dim dist As SqlInt32 = ComputeLevenstheinDistance(s1, s2)
        Dim maxLen As Integer = If(s1.Length > s2.Length, s1.Length, s2.Length)
        If maxLen = 0 Then Return 1.0F
        Return 1.0F - Convert.ToDouble(dist.Value) / Convert.ToDouble(maxLen)
    End Function

    ''' <summary>
    ''' Removes all non-alpha numeric characters and then sorts
    ''' the words in alphabetical order.
    ''' </summary>
    Private Shared Function GetSimilarityString(s1 As String) As String
        Dim normString = Regex.Replace(If(s1, ""), "\W|_", " ", Xms)
        normString = Regex.Replace(normString, "\s+", " ", Xms).Trim()
        Dim words As New List(Of String)(normString.Split(" "c))
        words.Sort()
        Return String.Join(" ", words.ToArray())
    End Function

End Class
1mrurvl1

1mrurvl13#

select id, title
from my_table
where 
    title like 'Aliens%' 
    and 
    len(rtrim(title)) < len('Aliens') + 7
xhv8bpkk

xhv8bpkk4#

根据你的提问,我想你要找的区别不应该超过原标题末尾的一个单词。这就是为什么1、2和4被返回的原因吗?
无论如何,我已经做了一个查询,检查末尾的差异由一个单词组成,没有空格。

declare @title varchar(20)
set @title = 'Aliens'
select id, title
from movies with (nolock)
where ltrim(title) like @title + '%'
and Charindex(' ', ltrim(right(title, len(title) - len(@title)))) = 0
and len(ltrim(right(title, len(title) - len(@title)))) < 7

希望能有所帮助。

esbemjvw

esbemjvw5#

如果您使用的是sql server 2008,则应该能够使用fullTEXT功能。
基本步骤如下:
1)在列上创建全文索引。这将标记每个字符串(stremmers、splitters等),并允许您搜索“LIKE THIS”字符串。
免责声明是,我从来没有使用过它,但我认为它可以做你想要的。
从这里开始阅读:http://msdn.microsoft.com/en-us/library/ms142571.aspx

yfjy0ee7

yfjy0ee76#

您可以尝试SSIS模糊分组,它会根据字符串匹配给予您打分。

ndasle7k

ndasle7k7#

您也可以在Oracle中使用utl_match。enter link description here

相关问题