Sql q-gram近似匹配优化

Sql q-gram近似匹配优化,sql,sql-server,fuzzy-search,fuzzy-comparison,Sql,Sql Server,Fuzzy Search,Fuzzy Comparison,我有一个包含300万个人记录的表,我想使用q-grams(例如姓氏)对其执行模糊匹配。我已经创建了一个包含2克链接的表,但是在这个数据量上(大约5分钟)搜索性能不是很好 我基本上有两个问题: (1) 您是否可以提出任何提高性能的方法,以避免表扫描(即必须计算搜索字符串和300万姓氏之间的常见q-gram) (2) 对于q-grams,如果A与B相似,C与B相似,这是否意味着C与A相似 问候 Peter我最近一直在研究模糊字符串匹配,所以即使冒着回答一个被抛弃的问题的风险,还是来了。希望你觉得这个

我有一个包含300万个人记录的表,我想使用q-grams(例如姓氏)对其执行模糊匹配。我已经创建了一个包含2克链接的表,但是在这个数据量上(大约5分钟)搜索性能不是很好

我基本上有两个问题: (1) 您是否可以提出任何提高性能的方法,以避免表扫描(即必须计算搜索字符串和300万姓氏之间的常见q-gram) (2) 对于q-grams,如果A与B相似,C与B相似,这是否意味着C与A相似

问候


Peter

我最近一直在研究模糊字符串匹配,所以即使冒着回答一个被抛弃的问题的风险,还是来了。希望你觉得这个有用

我想您只对编辑距离小于给定值的字符串感兴趣。你的q-克(或n-克)是这样的

2-grams for "foobar": {"fo","oo","ob","ba","ar"}
  • 您可以使用位置q图:

    位置信息可用于确定是否存在匹配 q-gram真的是一对“好搭档”

    例如,如果您正在搜索 具有最大编辑距离的“foobar” 对于2,这意味着你只是 感兴趣的词在哪里

    2-gram "fo" exists in with position from 1 to 3 or
    2-gram "oo" exists in with position from 2 to 4 or
    ... and so on
    
    字符串“barfoo”没有得到任何 匹配,因为 否则,匹配的2克相差 三,

  • 此外,使用 编辑距离的关系 和匹配的q-克的计数。 直觉是这样的

    字符串s有len(s)-q+1q-grams

    一次编辑操作最多可以影响q-grams

    我们可以推断

    编辑距离d内的字符串s1和s2至少具有 max(len(s1),len(s2))-q+1-qk匹配非位置q-grams

    如果您正在搜索“foobar” 最大编辑距离为2时,匹配 7个字符的字符串(例如 “fotocar”)应至少包含 两个普通的2克

  • 最后,显而易见的是 到按长度过滤。编辑 两条线之间的距离为 最小长度差 弦乐。例如,如果您的 阈值为2,您可以搜索 “foobar”、“foobbarb”不能 显然是一场比赛

  • 请参阅以获取更多和一些伪SQL。

    关于索引DNA q-grams的有趣文章,这样您就不必扫描整个表:


    www.comp.nus.edu.sg/~atung/publication/qgram_edit.pdf

    你肯定到处都能看到模糊文本搜索。例如,您键入“stck”,但实际上是指“stack”!有没有想过这些东西是怎么工作的

    有很多算法可以进行模糊文本匹配,每种算法都有各自的优缺点。最著名的是编辑距离和qgram。今天我想重点讨论QG并实现一个示例

    基本上,Qgram是关系数据库最合适的模糊字符串匹配算法。这很简单。qgram中的“q”将替换为2-gram、3-gram甚至4-gram等数字

    2-gram表示每个单词被分成一组两个字符的gram。“堆栈”将被分解为一组{“st”、“ta”、“ac”、“ck”}或“数据库”将被分解为{“da”、“at”、“ta”、“ab”、“ba”、“as”、“se”}

    一旦单词被分解成2克,我们就可以在数据库中搜索一组值,而不是一个字符串。例如,如果用户错误地键入了“stck”,任何对“stck”的搜索都将与“stack”不匹配,因为“a”丢失,但是2-gram集合{“st”,“tc”,“ck”}与2-gram集合的堆栈共有2行!宾果,我们找到了一个非常接近的匹配。它与2-gram的数据库集没有任何共同之处,与2-gram的“stat”集只有1个共同之处,因此我们可以很容易地建议用户输入:第一个是“stack”,第二个是“star”

    现在让我们使用SQLServer实现它:假设一个假设的Word数据集。你需要在图形和文字之间建立多对多的关系

    CREATE TABLE Grams(twog char(2), wordId int, PRIMARY KEY (twog, wordId))
    
    Grams表应该在前两个G上进行集群,然后在wordId上进行集群以提高性能。当你查询一个单词(例如堆栈)时,你把克放在一个临时表中。首先让我们创建几百万个虚拟记录

    --make millions of 2grams
     DECLARE @i int =0
     WHILE (@i<5000000)
     BEGIN
    -- a random 2gram
     declare @rnum1 char = CHAR(CAST(RAND()*28 AS INT)+97)
     declare @rnum2 char = CHAR(CAST(RAND()*28 AS INT)+97)
     INS... INTO Grams (twog, wordId) VALUES ( @rnum1 + @rnum2, CAST(RAND()*100000 AS int))
     END
    
    现在让我们查询单词“stack”,它将被分解为:{'st'、'ta'、'ac'、'ck'}两克

    DECLARE @word TABLE(twog char(2)) -- 'stack'
     INS... INTO @word VALUES ('st'), ('ta'), ('ac'), ('ck')
    
    select wordId, count(*) from @word w inner join Grams g ON w.twog = g.twog
     GROUP BY wordId
    

    您应该确保Sql Server在运行此查询时使用了一组聚集索引查找(或lockup)。这应该是自然的选择,但有时统计数据可能已损坏或过时,SqlServer可能会认为完整扫描更便宜。如果它不知道左侧表的基数,通常会发生这种情况,例如SqlServer可能会认为@word表是巨大的,数百万次的错误将比完整索引扫描更昂贵。

    我有一个简单的改进,它不会消除扫描,但是,如果你只使用2克或3克的话,加快速度:用数字替换字母。大多数SQL引擎在比较数字时工作得更快

    示例:我们的源表在一列中包含文本条目。 我们创建一个临时表,在其中使用

    SELECT SUBSTRING (column, 1,2) as gram, 1 as position FROM sourcetable
    UNION  
    SELECT SUBSTRING (column, 2,2) as gram, 2 as position FROM sourcetable
    UNION
    SELECT SUBSTRING (column, 3,2) as gram, 3 as position FROM sourcetable
    
    etc. 
    
    这应该在循环中运行,其中i=0,j=源条目的最大大小

    然后我们准备一个映射表,其中包含所有可能的两个字母的gram,并包含一个名为gram_id的标识(1,1)列。我们可以在英语词典中按频率对gram进行排序,并删除最不频繁的gram(如“kk”或“wq”)-此排序可能需要一些时间和研究,但它会将最小的数字分配给最频繁的gram,如果我们可以将gram的数量限制为255,这将提高性能,因为我们可以使用tinyint列作为gram_id

    然后我们从第一个temp表重建另一个temp表,在这里我们使用gram_id而不是gram。这将成为主表。我们在gram_id列和position列上创建索引

    然后,当我们必须将文本字符串与主表进行比较时,我们首先将文本字符串拆分为2克,然后替换2克
    SELECT SUBSTRING (column, 1,2) as gram, 1 as position FROM sourcetable
    UNION  
    SELECT SUBSTRING (column, 2,2) as gram, 2 as position FROM sourcetable
    UNION
    SELECT SUBSTRING (column, 3,2) as gram, 3 as position FROM sourcetable
    
    etc.