ระยะ Levenshtein ใน T-SQL


คำตอบ:


101

ฉันใช้ฟังก์ชันแก้ไขระยะทาง Levenshtein มาตรฐานใน TSQL ด้วยการเพิ่มประสิทธิภาพหลายอย่างที่ช่วยเพิ่มความเร็วในเวอร์ชันอื่น ๆ ที่ฉันรู้จัก ในกรณีที่สตริงทั้งสองมีอักขระเหมือนกันที่จุดเริ่มต้น (คำนำหน้าที่แบ่งใช้) อักขระที่เหมือนกันที่ส่วนท้าย (คำต่อท้ายที่ใช้ร่วมกัน) และเมื่อสตริงมีขนาดใหญ่และมีระยะการแก้ไขสูงสุดการปรับปรุงความเร็วจึงมีนัยสำคัญ ตัวอย่างเช่นเมื่ออินพุตเป็นสตริงอักขระ 4000 อักขระที่คล้ายกันมากสองสตริงและระบุระยะการแก้ไขสูงสุดเป็น 2 ค่าขนาดนี้จะเร็วกว่าคำสั่งขนาดเกือบสามedit_distance_withinฟังก์ชันในคำตอบที่ยอมรับโดยส่งคืนคำตอบใน 0.073 วินาที (73 มิลลิวินาที) เทียบกับ 55 วินาที นอกจากนี้ยังมีหน่วยความจำที่มีประสิทธิภาพโดยใช้พื้นที่เท่ากับสตริงอินพุตที่ใหญ่กว่าบวกกับพื้นที่คงที่ มันใช้ nvarchar "array" ตัวเดียวแทนคอลัมน์และทำการคำนวณทั้งหมดในตำแหน่งนั้นรวมทั้งตัวแปร int ตัวช่วยบางตัว

การเพิ่มประสิทธิภาพ:

  • ข้ามการประมวลผลคำนำหน้าและ / หรือคำต่อท้ายที่ใช้ร่วมกัน
  • ผลตอบแทนก่อนกำหนดหากสตริงขนาดใหญ่เริ่มต้นหรือลงท้ายด้วยสตริงที่เล็กกว่าทั้งหมด
  • ผลตอบแทนก่อนกำหนดหากความแตกต่างของขนาดรับประกันว่าจะเกินระยะทางสูงสุด
  • ใช้เพียงอาร์เรย์เดียวที่แสดงคอลัมน์ในเมทริกซ์ (ใช้งานเป็น nvarchar)
  • เมื่อกำหนดระยะทางสูงสุดความซับซ้อนของเวลาจะเปลี่ยนจาก (len1 * len2) ถึง (min (len1, len2)) เช่น linear
  • เมื่อกำหนดระยะทางสูงสุดผลตอบแทนก่อนกำหนดทันทีที่ทราบว่าขอบเขตระยะทางสูงสุดไม่สามารถทำได้

นี่คือรหัส (อัปเดต 1/20/2014 เพื่อเพิ่มความเร็วขึ้นอีกเล็กน้อย):

-- =============================================
-- Computes and returns the Levenshtein edit distance between two strings, i.e. the
-- number of insertion, deletion, and sustitution edits required to transform one
-- string to the other, or NULL if @max is exceeded. Comparisons use the case-
-- sensitivity configured in SQL Server (case-insensitive by default).
-- 
-- Based on Sten Hjelmqvist's "Fast, memory efficient" algorithm, described
-- at http://www.codeproject.com/Articles/13525/Fast-memory-efficient-Levenshtein-algorithm,
-- with some additional optimizations.
-- =============================================
CREATE FUNCTION [dbo].[Levenshtein](
    @s nvarchar(4000)
  , @t nvarchar(4000)
  , @max int
)
RETURNS int
WITH SCHEMABINDING
AS
BEGIN
    DECLARE @distance int = 0 -- return variable
          , @v0 nvarchar(4000)-- running scratchpad for storing computed distances
          , @start int = 1      -- index (1 based) of first non-matching character between the two string
          , @i int, @j int      -- loop counters: i for s string and j for t string
          , @diag int          -- distance in cell diagonally above and left if we were using an m by n matrix
          , @left int          -- distance in cell to the left if we were using an m by n matrix
          , @sChar nchar      -- character at index i from s string
          , @thisJ int          -- temporary storage of @j to allow SELECT combining
          , @jOffset int      -- offset used to calculate starting value for j loop
          , @jEnd int          -- ending value for j loop (stopping point for processing a column)
          -- get input string lengths including any trailing spaces (which SQL Server would otherwise ignore)
          , @sLen int = datalength(@s) / datalength(left(left(@s, 1) + '.', 1))    -- length of smaller string
          , @tLen int = datalength(@t) / datalength(left(left(@t, 1) + '.', 1))    -- length of larger string
          , @lenDiff int      -- difference in length between the two strings
    -- if strings of different lengths, ensure shorter string is in s. This can result in a little
    -- faster speed by spending more time spinning just the inner loop during the main processing.
    IF (@sLen > @tLen) BEGIN
        SELECT @v0 = @s, @i = @sLen -- temporarily use v0 for swap
        SELECT @s = @t, @sLen = @tLen
        SELECT @t = @v0, @tLen = @i
    END
    SELECT @max = ISNULL(@max, @tLen)
         , @lenDiff = @tLen - @sLen
    IF @lenDiff > @max RETURN NULL

    -- suffix common to both strings can be ignored
    WHILE(@sLen > 0 AND SUBSTRING(@s, @sLen, 1) = SUBSTRING(@t, @tLen, 1))
        SELECT @sLen = @sLen - 1, @tLen = @tLen - 1

    IF (@sLen = 0) RETURN @tLen

    -- prefix common to both strings can be ignored
    WHILE (@start < @sLen AND SUBSTRING(@s, @start, 1) = SUBSTRING(@t, @start, 1)) 
        SELECT @start = @start + 1
    IF (@start > 1) BEGIN
        SELECT @sLen = @sLen - (@start - 1)
             , @tLen = @tLen - (@start - 1)

        -- if all of shorter string matches prefix and/or suffix of longer string, then
        -- edit distance is just the delete of additional characters present in longer string
        IF (@sLen <= 0) RETURN @tLen

        SELECT @s = SUBSTRING(@s, @start, @sLen)
             , @t = SUBSTRING(@t, @start, @tLen)
    END

    -- initialize v0 array of distances
    SELECT @v0 = '', @j = 1
    WHILE (@j <= @tLen) BEGIN
        SELECT @v0 = @v0 + NCHAR(CASE WHEN @j > @max THEN @max ELSE @j END)
        SELECT @j = @j + 1
    END

    SELECT @jOffset = @max - @lenDiff
         , @i = 1
    WHILE (@i <= @sLen) BEGIN
        SELECT @distance = @i
             , @diag = @i - 1
             , @sChar = SUBSTRING(@s, @i, 1)
             -- no need to look beyond window of upper left diagonal (@i) + @max cells
             -- and the lower right diagonal (@i - @lenDiff) - @max cells
             , @j = CASE WHEN @i <= @jOffset THEN 1 ELSE @i - @jOffset END
             , @jEnd = CASE WHEN @i + @max >= @tLen THEN @tLen ELSE @i + @max END
        WHILE (@j <= @jEnd) BEGIN
            -- at this point, @distance holds the previous value (the cell above if we were using an m by n matrix)
            SELECT @left = UNICODE(SUBSTRING(@v0, @j, 1))
                 , @thisJ = @j
            SELECT @distance = 
                CASE WHEN (@sChar = SUBSTRING(@t, @j, 1)) THEN @diag                    --match, no change
                     ELSE 1 + CASE WHEN @diag < @left AND @diag < @distance THEN @diag    --substitution
                                   WHEN @left < @distance THEN @left                    -- insertion
                                   ELSE @distance                                        -- deletion
                                END    END
            SELECT @v0 = STUFF(@v0, @thisJ, 1, NCHAR(@distance))
                 , @diag = @left
                 , @j = case when (@distance > @max) AND (@thisJ = @i + @lenDiff) then @jEnd + 2 else @thisJ + 1 end
        END
        SELECT @i = CASE WHEN @j > @jEnd + 1 THEN @sLen + 1 ELSE @i + 1 END
    END
    RETURN CASE WHEN @distance <= @max THEN @distance ELSE NULL END
END

ดังที่ได้กล่าวไว้ในความคิดเห็นของฟังก์ชันนี้ความอ่อนไหวของตัวพิมพ์เล็กและใหญ่ของการเปรียบเทียบอักขระจะเป็นไปตามการเปรียบเทียบที่มีผลบังคับใช้ ตามค่าเริ่มต้นการเปรียบเทียบของ SQL Server เป็นสิ่งที่จะทำให้เกิดการเปรียบเทียบแบบไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่ วิธีหนึ่งในการปรับเปลี่ยนฟังก์ชันนี้ให้คำนึงถึงตัวพิมพ์เล็กและใหญ่เสมอคือการเพิ่มการจัดเรียงเฉพาะให้กับสองตำแหน่งที่มีการเปรียบเทียบสตริง อย่างไรก็ตามฉันยังไม่ได้ทดสอบอย่างละเอียดโดยเฉพาะอย่างยิ่งสำหรับผลข้างเคียงเมื่อฐานข้อมูลใช้การเปรียบเทียบที่ไม่ใช่ค่าเริ่มต้น นี่คือวิธีที่ทั้งสองบรรทัดจะถูกเปลี่ยนเพื่อบังคับให้มีการเปรียบเทียบแบบคำนึงถึงขนาดตัวพิมพ์:

    -- prefix common to both strings can be ignored
    WHILE (@start < @sLen AND SUBSTRING(@s, @start, 1) = SUBSTRING(@t, @start, 1) COLLATE SQL_Latin1_General_Cp1_CS_AS) 

และ

            SELECT @distance = 
                CASE WHEN (@sChar = SUBSTRING(@t, @j, 1) COLLATE SQL_Latin1_General_Cp1_CS_AS) THEN @diag                    --match, no change

1
เราจะใช้สิ่งนี้เพื่อค้นหาสตริงที่ใกล้เคียงที่สุด 5 อันดับแรกในตารางได้อย่างไร ฉันหมายถึงสมมติว่าฉันมีตารางชื่อถนนที่มีแถว 10m ฉันป้อนชื่อถนนค้นหา แต่มีอักขระ 1 ตัวที่เขียนไม่ถูกต้อง ฉันจะค้นหาการแข่งขันที่ใกล้เคียงที่สุด 5 อันดับแรกพร้อมประสิทธิภาพสูงสุดได้อย่างไร
MonsterMMORPG

1
นอกจากกำลังดุร้าย (เปรียบเทียบที่อยู่ทั้งหมด) คุณทำไม่ได้ Levenshtein ไม่ใช่สิ่งที่สามารถใช้ประโยชน์จากดัชนีได้อย่างง่ายดาย หากคุณสามารถ จำกัด ผู้สมัครให้แคบลงเป็นชุดย่อยผ่านสิ่งที่สามารถจัดทำดัชนีได้เช่นรหัสไปรษณีย์สำหรับที่อยู่หรือรหัสการออกเสียงสำหรับชื่อตัวอย่างเช่น Levenshtein แบบนั้นในคำตอบที่นี่สามารถนำไปใช้กับ ชุดย่อย หากต้องการนำไปใช้กับทั้งชุดที่มีขนาดใหญ่คุณจะต้องไปที่ Levenshtein Automata แต่การใช้งานใน SQL นั้นอยู่นอกเหนือขอบเขตของคำถาม SO ที่จะตอบที่นี่
ขวาน - ทำด้วย SOverflow

@MonsterMMORPG ในทางทฤษฎีคุณสามารถย้อนกลับและคำนวณการเรียงสับเปลี่ยนที่เป็นไปได้ทั้งหมดสำหรับระยะทาง Levenshtein ที่กำหนด หรือคุณอาจลองดูว่าคำในที่อยู่ของคุณเป็นรายการที่สั้นพอที่จะเป็นประโยชน์หรือไม่ (อาจไม่สนใจคำที่ไม่ค่อยปรากฏ)
TheConstructor

@MonsterMMORPG - นี่มันสายไป แต่ฉันคิดว่าฉันจะเพิ่มคำตอบที่ดีกว่านี้ หากคุณทราบจำนวนการแก้ไขขั้นต่ำที่คุณจะอนุญาตคุณสามารถใช้วิธี Symmetric Delete เหมือนที่เคยทำในโครงการ symspell บน github คุณสามารถจัดเก็บการเรียงสับเปลี่ยนย่อยของการลบเพียงเล็กน้อยจากนั้นค้นหาการเรียงสับเปลี่ยนการลบชุดเล็ก ๆ ของสตริงการค้นหา ในชุดที่ส่งคืน (ซึ่งจะมีขนาดเล็กหากคุณอนุญาตให้มีระยะการแก้ไขสูงสุด 1 หรือ 2 เท่านั้น) จากนั้นคุณจะคำนวณ levenshtein แบบเต็ม แต่นั่นควรมากน้อยกว่าการทำในทุกสาย
ขวาน - ทำด้วย SOverflow

1
@DaveCousineau - ดังที่กล่าวไว้ในข้อคิดเห็นของฟังก์ชันการเปรียบเทียบสตริงใช้ความไวของตัวพิมพ์สำหรับการเปรียบเทียบ SQL Server ที่มีผล ตามค่าเริ่มต้นโดยทั่วไปหมายถึงไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่ ดูการแก้ไขโพสต์ของฉันที่ฉันเพิ่งเพิ่ม การใช้งาน Fribble ในคำตอบอื่นจะทำงานในทำนองเดียวกันเกี่ยวกับการเปรียบเทียบ
ขวาน - ทำด้วย SOverflow

58

Arnold Fribble มีข้อเสนอสองข้อในsqlteam.com/forums

นี่คือน้องจากปี 2006:

SET QUOTED_IDENTIFIER ON 
GO
SET ANSI_NULLS ON 
GO

CREATE FUNCTION edit_distance_within(@s nvarchar(4000), @t nvarchar(4000), @d int)
RETURNS int
AS
BEGIN
  DECLARE @sl int, @tl int, @i int, @j int, @sc nchar, @c int, @c1 int,
    @cv0 nvarchar(4000), @cv1 nvarchar(4000), @cmin int
  SELECT @sl = LEN(@s), @tl = LEN(@t), @cv1 = '', @j = 1, @i = 1, @c = 0
  WHILE @j <= @tl
    SELECT @cv1 = @cv1 + NCHAR(@j), @j = @j + 1
  WHILE @i <= @sl
  BEGIN
    SELECT @sc = SUBSTRING(@s, @i, 1), @c1 = @i, @c = @i, @cv0 = '', @j = 1, @cmin = 4000
    WHILE @j <= @tl
    BEGIN
      SET @c = @c + 1
      SET @c1 = @c1 - CASE WHEN @sc = SUBSTRING(@t, @j, 1) THEN 1 ELSE 0 END
      IF @c > @c1 SET @c = @c1
      SET @c1 = UNICODE(SUBSTRING(@cv1, @j, 1)) + 1
      IF @c > @c1 SET @c = @c1
      IF @c < @cmin SET @cmin = @c
      SELECT @cv0 = @cv0 + NCHAR(@c), @j = @j + 1
    END
    IF @cmin > @d BREAK
    SELECT @cv1 = @cv0, @i = @i + 1
  END
  RETURN CASE WHEN @cmin <= @d AND @c <= @d THEN @c ELSE -1 END
END
GO

1
@ อเล็กซานเดอร์ดูเหมือนจะใช้งานได้ แต่ฉันจะเปลี่ยนชื่อตัวแปรของคุณให้มีความหมายมากขึ้น นอกจากนี้ฉันจะกำจัด @d คุณรู้ความยาวของสองสตริงในอินพุตของคุณ
Lieven Keersmaekers

2
@ ลีเวน: ไม่ใช่การใช้งานของฉันผู้เขียนคือ Arnold Fribble พารามิเตอร์ @d คือความแตกต่างสูงสุดที่อนุญาตระหว่างสตริงหลังจากถึงซึ่งถือว่าหลากหลายเกินไปและฟังก์ชันจะส่งกลับ -1 เพิ่มเนื่องจากอัลกอริทึมใน T-SQL ทำงานช้าเกินไป
Alexander Prokofyev

คุณควรตรวจสอบรหัสอัลกอริทึม psuedo ที่: en.wikipedia.org/wiki/Levenshtein_distanceมันไม่ได้ดีขึ้นมากนัก
Norman H

13

IIRC กับ SQL Server 2005 และขั้นตอนต่อมาคุณสามารถเขียนเก็บไว้ในภาษา .NET ใดใช้บูรณาการ CLR ใน SQL Server 2005 กับที่มันไม่ควรจะยากที่จะเขียนขั้นตอนสำหรับการคำนวณระยะทาง Levenstein

ง่ายๆสวัสดีชาวโลก! สกัดจากความช่วยเหลือ:

using System;
using System.Data;
using Microsoft.SqlServer.Server;
using System.Data.SqlTypes;

public class HelloWorldProc
{
    [Microsoft.SqlServer.Server.SqlProcedure]
    public static void HelloWorld(out string text)
    {
        SqlContext.Pipe.Send("Hello world!" + Environment.NewLine);
        text = "Hello world!";
    }
}

จากนั้นใน SQL Server ของคุณให้เรียกใช้สิ่งต่อไปนี้:

CREATE ASSEMBLY helloworld from 'c:\helloworld.dll' WITH PERMISSION_SET = SAFE

CREATE PROCEDURE hello
@i nchar(25) OUTPUT
AS
EXTERNAL NAME helloworld.HelloWorldProc.HelloWorld

และตอนนี้คุณสามารถทดสอบการทำงานได้:

DECLARE @J nchar(25)
EXEC hello @J out
PRINT @J

หวังว่านี่จะช่วยได้


7

คุณสามารถใช้ Levenshtein Distance Algorithm เพื่อเปรียบเทียบสตริง

คุณสามารถค้นหาตัวอย่าง T-SQL ได้ที่http://www.kodyaz.com/articles/fuzzy-string-matching-using-levenshtein-distance-sql-server.aspx

CREATE FUNCTION edit_distance(@s1 nvarchar(3999), @s2 nvarchar(3999))
RETURNS int
AS
BEGIN
 DECLARE @s1_len int, @s2_len int
 DECLARE @i int, @j int, @s1_char nchar, @c int, @c_temp int
 DECLARE @cv0 varbinary(8000), @cv1 varbinary(8000)

 SELECT
  @s1_len = LEN(@s1),
  @s2_len = LEN(@s2),
  @cv1 = 0x0000,
  @j = 1, @i = 1, @c = 0

 WHILE @j <= @s2_len
  SELECT @cv1 = @cv1 + CAST(@j AS binary(2)), @j = @j + 1

 WHILE @i <= @s1_len
 BEGIN
  SELECT
   @s1_char = SUBSTRING(@s1, @i, 1),
   @c = @i,
   @cv0 = CAST(@i AS binary(2)),
   @j = 1

  WHILE @j <= @s2_len
  BEGIN
   SET @c = @c + 1
   SET @c_temp = CAST(SUBSTRING(@cv1, @j+@j-1, 2) AS int) +
    CASE WHEN @s1_char = SUBSTRING(@s2, @j, 1) THEN 0 ELSE 1 END
   IF @c > @c_temp SET @c = @c_temp
   SET @c_temp = CAST(SUBSTRING(@cv1, @j+@j+1, 2) AS int)+1
   IF @c > @c_temp SET @c = @c_temp
   SELECT @cv0 = @cv0 + CAST(@c AS binary(2)), @j = @j + 1
 END

 SELECT @cv1 = @cv0, @i = @i + 1
 END

 RETURN @c
END

(ฟังก์ชั่นที่พัฒนาโดย Joseph Gama)

การใช้งาน:

select
 dbo.edit_distance('Fuzzy String Match','fuzzy string match'),
 dbo.edit_distance('fuzzy','fuzy'),
 dbo.edit_distance('Fuzzy String Match','fuzy string match'),
 dbo.edit_distance('levenshtein distance sql','levenshtein sql server'),
 dbo.edit_distance('distance','server')

อัลกอริทึมเพียงแค่ส่งคืนจำนวน stpe เพื่อเปลี่ยนสตริงหนึ่งเป็นสตริงอื่นโดยแทนที่อักขระอื่นในขั้นตอนเดียว


น่าเสียดายที่ไม่ครอบคลุมกรณีที่สตริงว่าง
Codeman

2

ฉันกำลังมองหาตัวอย่างโค้ดสำหรับอัลกอริทึม Levenshtein ด้วยและยินดีที่จะพบมันที่นี่ แน่นอนผมอยากที่จะเข้าใจว่าอัลกอริทึมที่ทำงานและผมเล่นรอบ ๆ นิด ๆ หน่อย ๆ กับหนึ่งในตัวอย่างข้างต้นที่ผมได้เล่นรอบนิด ๆ หน่อย ๆ ที่ถูกโพสต์โดยVeve เพื่อให้เข้าใจโค้ดได้ดีขึ้นฉันได้สร้าง EXCEL ด้วยเมทริกซ์

ระยะทางของ FUZZY เทียบกับ FUZY

รูปภาพพูดได้มากกว่า 1,000 คำ

ด้วย EXCEL นี้ฉันพบว่ามีศักยภาพในการเพิ่มประสิทธิภาพการทำงานเพิ่มเติม ไม่จำเป็นต้องคำนวณค่าทั้งหมดในพื้นที่สีแดงด้านขวาบน ค่าของเซลล์สีแดงแต่ละเซลล์จะส่งผลให้ค่าของเซลล์ด้านซ้ายบวก 1 เนื่องจากสตริงที่สองจะยาวกว่าในพื้นที่นั้นเสมอซึ่งจะทำให้ระยะห่างเพิ่มขึ้นด้วยค่า 1 สำหรับแต่ละอักขระ

คุณสามารถสะท้อนสิ่งนั้นได้โดยใช้คำสั่งIF @j <= @iและเพิ่มค่า@iก่อนหน้าคำสั่งนี้

CREATE FUNCTION [dbo].[f_LevenshteinDistance](@s1 nvarchar(3999), @s2 nvarchar(3999))
    RETURNS int
    AS
    BEGIN
       DECLARE @s1_len  int;
       DECLARE @s2_len  int;
       DECLARE @i       int;
       DECLARE @j       int;
       DECLARE @s1_char nchar;
       DECLARE @c       int;
       DECLARE @c_temp  int;
       DECLARE @cv0     varbinary(8000);
       DECLARE @cv1     varbinary(8000);

       SELECT
          @s1_len = LEN(@s1),
          @s2_len = LEN(@s2),
          @cv1    = 0x0000  ,
          @j      = 1       , 
          @i      = 1       , 
          @c      = 0

       WHILE @j <= @s2_len
          SELECT @cv1 = @cv1 + CAST(@j AS binary(2)), @j = @j + 1;

          WHILE @i <= @s1_len
             BEGIN
                SELECT
                   @s1_char = SUBSTRING(@s1, @i, 1),
                   @c       = @i                   ,
                   @cv0     = CAST(@i AS binary(2)),
                   @j       = 1;

                SET @i = @i + 1;

                WHILE @j <= @s2_len
                   BEGIN
                      SET @c = @c + 1;

                      IF @j <= @i 
                         BEGIN
                            SET @c_temp = CAST(SUBSTRING(@cv1, @j + @j - 1, 2) AS int) + CASE WHEN @s1_char = SUBSTRING(@s2, @j, 1) THEN 0 ELSE 1 END;
                            IF @c > @c_temp SET @c = @c_temp
                            SET @c_temp = CAST(SUBSTRING(@cv1, @j + @j + 1, 2) AS int) + 1;
                            IF @c > @c_temp SET @c = @c_temp;
                         END;
                      SELECT @cv0 = @cv0 + CAST(@c AS binary(2)), @j = @j + 1;
                   END;
                SET @cv1 = @cv0;
          END;
       RETURN @c;
    END;

ตามที่เขียนไว้สิ่งนี้จะไม่ให้ผลลัพธ์ที่ถูกต้องเสมอไป ยกตัวอย่างเช่นปัจจัยการผลิต('jane', 'jeanne')จะกลับมาเป็นระยะทาง 3 เมื่อระยะทางที่ควรจะเป็น 2. หากต้องการแก้ไขรหัสเพิ่มเติมนี้ควรจะเสริมว่าการแลกเปลี่ยน@s1และ@s2ถ้ามีความยาวสั้นกว่า@s1 @s2
ขวาน - ทำด้วย SOverflow

2

ใน TSQL วิธีที่ดีที่สุดและเร็วที่สุดในการเปรียบเทียบสองรายการคือคำสั่ง SELECT ที่เข้าร่วมตารางในคอลัมน์ที่จัดทำดัชนี ดังนั้นนี่คือวิธีที่ฉันแนะนำให้ใช้ระยะการแก้ไขหากคุณต้องการได้รับประโยชน์จากข้อได้เปรียบของเอ็นจิ้น RDBMS TSQL Loops ก็ใช้ได้เช่นกัน แต่การคำนวณระยะทางของ Levenstein จะเร็วกว่าในภาษาอื่น ๆ มากกว่าใน TSQL สำหรับการเปรียบเทียบปริมาณมาก

ฉันใช้ระยะการแก้ไขในหลายระบบโดยใช้ชุดการเข้าร่วมกับตารางชั่วคราวที่ออกแบบมาเพื่อจุดประสงค์นั้นเท่านั้น ต้องใช้ขั้นตอนก่อนการประมวลผลที่หนักหน่วง - การจัดเตรียมตารางชั่วคราว - แต่ใช้ได้ดีกับการเปรียบเทียบจำนวนมาก

พูดไม่กี่คำ: การประมวลผลล่วงหน้าประกอบด้วยการสร้างการเติมข้อมูลและการจัดทำดัชนีตารางอุณหภูมิ รายการแรกประกอบด้วยรหัสอ้างอิงคอลัมน์ตัวอักษรเดียวและคอลัมน์เสน่ห์ ตารางนี้สร้างขึ้นโดยการเรียกใช้ชุดคำสั่งแทรกที่แยกทุกคำออกเป็นตัวอักษร (โดยใช้ SELECT SUBSTRING) เพื่อสร้างแถวให้มากที่สุดเท่าที่คำในรายการแหล่งที่มาจะมีตัวอักษร (ฉันรู้ว่าเป็นแถวจำนวนมาก แต่เซิร์ฟเวอร์ SQL สามารถรองรับได้หลายพันล้าน ของแถว) จากนั้นสร้างตารางที่สองด้วยคอลัมน์ 2 ตัวอักษรตารางอื่นที่มีคอลัมน์ 3 ตัวอักษรเป็นต้นผลลัพธ์สุดท้ายคือชุดของตารางที่มีรหัสอ้างอิงและสตริงย่อยของแต่ละคำรวมทั้งการอ้างอิงตำแหน่ง ในคำ

เมื่อเสร็จแล้วเกมทั้งหมดจะเกี่ยวกับการทำซ้ำตารางเหล่านี้และรวมเข้ากับตารางที่ซ้ำกันในแบบสอบถามเลือก GROUP BY ซึ่งจะนับจำนวนการแข่งขัน สิ่งนี้จะสร้างชุดของการวัดสำหรับทุกคู่คำที่เป็นไปได้ซึ่งจะรวมใหม่เป็นระยะทางเลเวนสไตน์เดียวต่อคู่ของคำ

ในทางเทคนิคแล้วสิ่งนี้แตกต่างจากการใช้งานระยะทาง Levenstein (หรือตัวแปรอื่น ๆ ) มากที่สุดดังนั้นคุณต้องเข้าใจอย่างลึกซึ้งว่าระยะทาง Levenstein ทำงานอย่างไรและเหตุใดจึงออกแบบตามที่เป็น ตรวจสอบทางเลือกอื่น ๆ ด้วยเพราะด้วยวิธีการดังกล่าวคุณจะได้ชุดของเมตริกพื้นฐานซึ่งสามารถช่วยคำนวณระยะการแก้ไขรูปแบบต่างๆได้ในเวลาเดียวกันทำให้คุณได้รับการปรับปรุงศักยภาพในการเรียนรู้ของเครื่องที่น่าสนใจ

อีกประเด็นหนึ่งที่กล่าวถึงในคำตอบก่อนหน้าในหน้านี้: พยายามดำเนินการล่วงหน้าให้มากที่สุดเพื่อกำจัดคู่ที่ไม่ต้องการการวัดระยะทาง ตัวอย่างเช่นคู่ของคำสองคำที่ไม่มีตัวอักษรเหมือนกันควรถูกแยกออกเนื่องจากระยะการแก้ไขสามารถหาได้จากความยาวของสตริง หรืออย่าวัดระยะห่างระหว่างสองสำเนาของคำเดียวกันเนื่องจากเป็น 0 ตามธรรมชาติ หรือลบคำที่ซ้ำกันออกก่อนทำการวัดหากรายการคำของคุณมาจากข้อความยาวมีแนวโน้มว่าคำเดียวกันจะปรากฏมากกว่าหนึ่งครั้งดังนั้นการวัดระยะทางเพียงครั้งเดียวจะช่วยประหยัดเวลาในการประมวลผลเป็นต้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.