{"id":7689,"date":"2015-01-05T17:57:49","date_gmt":"2015-01-05T17:57:49","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/string-comparisons-in-sql-edit-distance-and-the-levenshtein-algorithm\/"},"modified":"2026-04-17T16:10:29","modified_gmt":"2026-04-17T16:10:29","slug":"string-comparisons-in-sql-edit-distance-and-the-levenshtein-algorithm","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/blogs\/string-comparisons-in-sql-edit-distance-and-the-levenshtein-algorithm\/","title":{"rendered":"Levenshtein Distance in SQL Server: Edit Distance and String Similarity"},"content":{"rendered":"\n<div id=\"pretty\">\n<p><strong>Calculating how similar two strings are &#8211; rather than whether they are identical &#8211; requires an edit distance algorithm. The Levenshtein distance counts the minimum number of single-character insertions, deletions, or substitutions needed to transform one string into another. In SQL Server, the best T-SQL implementation is the approach attributed to &#8216;Arnold Fribble&#8217;, which uses a compact string-based technique rather than a full matrix and performs reasonably for most datasets. <\/strong><\/p>\n<p><strong>For higher performance on larger datasets, a CLR assembly wrapping .NET&#8217;s string distance functions provides the Damerau-Levenshtein variant, which also accounts for transpositions. This article covers both approaches with working code and a practical recommendation on when to use each.<\/strong><\/p>\n<p>Sometimes you need to know how similar words are, rather than whether they are identical. To get a general measure of similarity is tricky, impossible probably, because similarity is so strongly determined by culture. The Soundex algorithm can come up with some matches but insists that, for example, &#8216;voluptuousness&#8217; and &#8216;velvet&#8217; are similar. Family genealogists in Britain would never find a search algorithm that helped them to realise that some branches of the Theobald family spell their surname &#8216;Tibble&#8217;, though Soundex knows they are quite close. To gauge this sort of similarity, you need to know the context of language, pronunciation, culture and semantics. However, we can fall back on more scientific measures based on comparing sequences, though they will never be perfect.<\/p>\n<p>&#8216;Edit distance&#8217; is the obvious way of measuring similarity of sequences of values, and strings are just sequences of characters. How many edits are needed to convert the first string into the second? Edits can be insertions, deletions, replacements or transpositions. Algorithms to do this &#8216;<a href=\"http:\/\/en.wikipedia.org\/wiki\/String-to-string_correction_problem\">string-to-string correction problem<\/a>&#8216; for any sequences have been current since the Sixties, and have become increasingly refined for a range of sciences such as genome research, Forensics, <a href=\"http:\/\/www.sciencedirect.com\/science\/article\/pii\/S1570866703000285\">Dendrochronology<\/a>, and for predictive text.<\/p>\n<p>The Dynamic approach to finding an edit distance is best done with a matrix. <a href=\"http:\/\/rosettacode.org\/wiki\/Levenshtein_distance\">Solutions for this<\/a> exist for all manner of languages. SQL is handicapped by having no support for matrices. An equivalent relation can be used instead, but it is overkill, and slow. We don&#8217;t need all the wonderful things a relation gives us. The fastest dynamic solutions in SQL tend to use Unicode strings as cod matrices since these can be cajoled into use as integer matrixes, but they are painful to use and debug.<\/p>\n<p>Fortunately, third-party <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/clr-assembly-regex-functions-for-sql-server-by-example\/\" target=\"_blank\" rel=\"noopener\">CLR functions exist<\/a> for calculating Damerau-Levenshtein Distance in SQL Server. Levenshtein Distance, developed by Vladimir Levenshtein in 1965, is the algorithm we learn in college for measuring edit-difference. It doesn&#8217;t deal perfectly with transpositions because it doesn&#8217;t even attempt to detect them: it records one transposition as two edits: an insertion and a deletion. The Damerau-Levenshtein algorithm for Edit Distance solves this.<\/p>\n<p>Here is a simple implementation of the Levenshtein algorithm, using the full matrix. In this version, I&#8217;ve restricted the length of string just so as to get a good performance. I doubled performance by removing the NVARCHAR(MAX)s. I is, hopefully, useful just to understand the algorithm, because once all the clever short-cuts are used, it can all get rather opaque.\u00a0<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">CREATE function LEVENSHTEIN ( @SourceString nvarchar(100), @TargetString nvarchar(100) ) \n--Returns the Levenshtein Distance between @SourceString string and @TargetString\n--Translated to TSQL by Joseph Gama\n--Updated slightly by Phil Factor\nreturns int\nas\nBEGIN\nDECLARE @Matrix Nvarchar(4000), @LD int, @TargetStringLength int, @SourceStringLength int,\n@ii int, @jj int, @CurrentSourceChar nchar(1), @CurrentTargetChar nchar(1),@Cost int, \n@Above int,@AboveAndToLeft int,@ToTheLeft int, @MinimumValueOfCells int\n-- Step 1: Set n to be the length of s. Set m to be the length of t. \n--                    If n = 0, return m and exit.\n--    If m = 0, return n and exit.\n--    Construct a matrix containing 0..m rows and 0..n columns.\nif @SourceString is null or @TargetString is null return null\nSelect @SourceStringLength=LEN(@SourceString), \n     @TargetStringLength=LEN(@TargetString),\n     @Matrix=replicate(nchar(0),(@SourceStringLength+1)*(@TargetStringLength+1))\nIf @SourceStringLength = 0 return @TargetStringLength\nIf @TargetStringLength = 0 return @SourceStringLength\nif (@TargetStringLength+1)*(@SourceStringLength+1)&gt; 4000 return -1\n--Step 2: Initialize the first row to 0..n.\n--     Initialize the first column to 0..m.\nSET @ii=0\nWHILE @ii&lt;=@SourceStringLength\n    BEGIN\n    SET @Matrix=STUFF(@Matrix,@ii+1,1,nchar(@ii))--d(i, 0) = i\n    SET @ii=@ii+1\n    END\nSET @ii=0\nWHILE @ii&lt;=@TargetStringLength\n    BEGIN\n    SET @Matrix=STUFF(@Matrix,@ii*(@SourceStringLength+1)+1,1,nchar(@ii))--d(0, j) = j\n    SET @ii=@ii+1\n    END\n--Step 3 Examine each character of s (i from 1 to n).\nSET @ii=1\nWHILE @ii&lt;=@SourceStringLength\n    BEGIN\n\n--Step 4   Examine each character of t (j from 1 to m).\n    SET @jj=1\n    WHILE @jj&lt;=@TargetStringLength\n        BEGIN\n--Step 5 and 6\n        Select \n        --Set cell d[i,j] of the matrix equal to the minimum of:\n        --a. The cell immediately above plus 1: d[i-1,j] + 1.\n        --b. The cell immediately to the left plus 1: d[i,j-1] + 1.\n        --c. The cell diagonally above and to the left plus the cost: d[i-1,j-1] + cost\n        @Above=unicode(substring(@Matrix,@jj*(@SourceStringLength+1)+@ii-1+1,1))+1,\n        @ToTheLeft=unicode(substring(@Matrix,(@jj-1)*(@SourceStringLength+1)+@ii+1,1))+1,\n        @AboveAndToLeft=unicode(substring(@Matrix,(@jj-1)*(@SourceStringLength+1)+@ii-1+1,1))\n         + case when (substring(@SourceString,@ii,1)) = (substring(@TargetString,@jj,1)) \n            then 0 else 1 end--the cost\n        -- If s[i] equals t[j], the cost is 0.\n      -- If s[i] doesn't equal t[j], the cost is 1.\n        -- now calculate the minimum value of the three\n        if (@Above &lt; @ToTheLeft) AND (@Above &lt; @AboveAndToLeft) \n            select @MinimumValueOfCells=@Above\n      else if (@ToTheLeft &lt; @Above) AND (@ToTheLeft &lt; @AboveAndToLeft)\n            select @MinimumValueOfCells=@ToTheLeft\n        else\n            select @MinimumValueOfCells=@AboveAndToLeft\n        Select @Matrix=STUFF(@Matrix,\n                   @jj*(@SourceStringLength+1)+@ii+1,1,\n                   nchar(@MinimumValueOfCells)),\n           @jj=@jj+1\n        END\n    SET @ii=@ii+1\n    END    \n--Step 7 After iteration steps (3, 4, 5, 6) are complete, distance is found in cell d[n,m]\nreturn unicode(substring(\n   @Matrix,@SourceStringLength*(@TargetStringLength+1)+@TargetStringLength+1,1\n   ))\nEND\ngo\n<\/pre>\n<p>Here is the same basic code with the additions to turn it into the\u00a0Damerau-Levenshtein algorithm.\u00a0<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true  \">CREATE function DamerauLevenschtein ( @SourceString nvarchar(100), @TargetString nvarchar(100) ) \n--Returns the Damerau Levenshtein Distance between @SourceString string and @TargetString\n--Updated by Phil Factor to add transposition as an edit\nreturns int\nas\nBEGIN\n--DECLARE  @SourceString nvarchar(100)='achieve', @TargetString nvarchar(100)='acheive'\nDECLARE @Matrix Nvarchar(4000), @LD int, @TargetStringLength int, @SourceStringLength int,\n@ii int, @jj int, @CurrentSourceChar nchar(1), @CurrentTargetChar nchar(1),@Cost int, \n@Above int,@AboveAndToLeft int,@ToTheLeft int, @MinimumValueOfCells INT, @previous INT\n\n-- Step 1: Set n to be the length of s. Set m to be the length of t. \n  SELECT @SourceString=RTRIM(LTRIM(COALESCE(@sourceString,''))),\n         @TargetString=RTRIM(LTRIM(COALESCE(@TargetString,''))),\n\t\t @SourceStringLength=LEN(@SourceString), \n         @TargetStringLength=LEN(@TargetString)\n\n  -- remove matches at the beginning and end\n  IF SUBSTRING(@sourceString,1,1)=SUBSTRING(@targetString,1,1)\n  BEGIN\n  SET @ii=1\n  WHILE SUBSTRING(@sourceString+'!!',@ii+1,1)=SUBSTRING(@targetString+'??',@ii+1,1)\n    BEGIN\n    SELECT @ii=@ii+1 \n    END\n  SELECT @sourceString=STUFF(@sourceString,1,@ii,''),\n         @targetString=STUFF(@targetString,1,@ii,'')\n  END\n \n SELECT @SourceStringLength =LEN(@sourceString), @TargetStringLength =LEN(@TargetString) \n IF SUBSTRING(@sourceString,@SourceStringLength,1)=SUBSTRING(@targetString,@TargetStringLength,1)\n  BEGIN\n  WHILE SUBSTRING(@sourceString,@SourceStringLength-1,1)=SUBSTRING(@targetString,@TargetStringLength-1,1) \n\tAND @SourceStringLength&gt;0 AND @TargetStringLength&gt;0\n    BEGIN\n    SELECT @SourceStringLength=@SourceStringLength-1,\n\t       @TargetStringLength=@TargetStringLength-1\n\tEND\n  SELECT @sourceString=LEFT(@sourceString,@SourceStringLength)\n  SELECT @targetString=LEFT(@targetString,@TargetStringLength)\n  END\n--    If n = 0, return m and exit.\n--    If m = 0, return n and exit.\nIf @SourceStringLength = 0 return @TargetStringLength\nIf @TargetStringLength = 0 return @SourceStringLength\nif (@TargetStringLength+1)*(@SourceStringLength+1)&gt; 4000 return -1\n  IF @SourceStringLength=1 \n    RETURN @TargetStringLength\n          -CASE WHEN CHARINDEX(@SourceString,@TargetString)&gt;0 THEN 1 ELSE 0 end\n  IF @TargetStringLength=1 \n    RETURN @SourceStringLength\n          -CASE WHEN CHARINDEX(@TargetString,@SourceString)&gt;0 THEN 1 ELSE 0 end\n--    Construct a matrix containing 0..m rows and 0..n columns.\nSELECT @Matrix=replicate(nchar(0),(@SourceStringLength+1)*(@TargetStringLength+1))\n--Step 2: Initialize the first row to 0..n.\n--     Initialize the first column to 0..m.\nSET @ii=0\nWHILE @ii&lt;=@SourceStringLength\n    BEGIN\n    SET @Matrix=STUFF(@Matrix,@ii+1,1,nchar(@ii))--d(i, 0) = i\n    SET @ii=@ii+1\n    END\nSET @ii=0\nWHILE @ii&lt;=@TargetStringLength\n    BEGIN\n    SET @Matrix=STUFF(@Matrix,@ii*(@SourceStringLength+1)+1,1,nchar(@ii))--d(0, j) = j\n    SET @ii=@ii+1\n    END\n--Step 3 Examine each character of s (i from 1 to n).\nSET @ii=1\nWHILE @ii&lt;=@SourceStringLength\n    BEGIN\n--Step 4   Examine each character of t (j from 1 to m).\n    SET @jj=1\n    WHILE @jj&lt;=@TargetStringLength\n        BEGIN\n--Step 5 and 6\n        Select \n        --Set cell d[i,j] of the matrix equal to the minimum of:\n        --a. The cell immediately above plus 1: d[i-1,j] + 1.\n        --b. The cell immediately to the left plus 1: d[i,j-1] + 1.\n        --c. The cell diagonally above and to the left plus the cost: d[i-1,j-1] + cost \n\t\t@Cost=case when (substring(@SourceString,@ii,1)) = (substring(@TargetString,@jj,1)) \n            then 0 else 1 END,--the cost\n        -- If s[i] equals t[j], the cost is 0.\n        -- If s[i] doesn't equal t[j], the cost is 1. \n        @Above         =unicode(substring(@Matrix, @jj *  (@SourceStringLength+1)+@ii-1+1,1))+1,\n        @ToTheLeft     =unicode(substring(@Matrix,(@jj-1)*(@SourceStringLength+1)+@ii+1  ,1))+1,\n        @AboveAndToLeft=unicode(substring(@Matrix,(@jj-1)*(@SourceStringLength+1)+@ii-1+1,1))+@cost,\n        @previous      =unicode(substring(@Matrix,(@jj-2)*(@SourceStringLength+1)+@ii-2+1,1))+@cost\n        -- now calculate the minimum value of the three\n        if (@Above &lt; @ToTheLeft) AND (@Above &lt; @AboveAndToLeft) \n            select @MinimumValueOfCells=@Above\n      else if (@ToTheLeft &lt; @Above) AND (@ToTheLeft &lt; @AboveAndToLeft)\n            select @MinimumValueOfCells=@ToTheLeft\n        else\n            select @MinimumValueOfCells=@AboveAndToLeft\n        IF (substring(@SourceString,@ii,1) = substring(@TargetString,@jj-1,1) \n              and substring(@TargetString,@jj,1) = substring(@SourceString,@ii-1,1))\n            begin\n\t\t\tSELECT @MinimumValueOfCells = \n\t\t\t  CASE WHEN @MinimumValueOfCells&lt; @previous \n\t\t\t\tTHEN @MinimumValueOfCells ELSE @previous END \n\t\t\t  end  \n\t\t\t  --write it to the matrix\n\t\tSELECT @Matrix=STUFF(@Matrix,\n                   @jj*(@SourceStringLength+1)+@ii+1,1,\n                   nchar(@MinimumValueOfCells)),\n           @jj=@jj+1\n        END\n    SET @ii=@ii+1\n    END    \n--Step 7 After iteration steps (3, 4, 5, 6) are complete, distance is found in cell d[n,m]\nreturn unicode(substring(\n   @Matrix,@SourceStringLength*(@TargetStringLength+1)+@TargetStringLength+1,1\n   ))\nend<\/pre>\n<p>The best SQL solution I know of for the Levenshtein algorithm is the one attributed pseudonymously to &#8216;Arnold Fribble&#8217; (possibly a reference to <a href=\"http:\/\/reddwarf.wikia.com\/wiki\/Mr._Flibble\">Arnold Rimmer<\/a> of Red Dwarf, and his &#8216;friend&#8217; Mr Flibble.) which is a SQL version of the improved Levenshtein algorithm that dispenses with the full matrix and just uses two vectors instead. <a href=\"https:\/\/www.simple-talk.com\/content\/article.aspx?article=435\">It is featured here<\/a>. There are improved versions around, such as <a href=\"http:\/\/blog.softwx.net\/2014\/12\/optimizing-levenshtein-algorithm-in-tsql.html\">the one here<\/a>. They take around two thirds of the time to produce their result compared with the full-matrix version.<\/p>\n<p>Can this be done in a relational way? Yes, but without support for matrixes, it is a bit painful, with the full-matrix version taking ten times as long as the string-based version. There is plenty of room for improvement though.<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">alter FUNCTION LevenschteinDifference\n(\n@FirstString nVarchar(255), @SecondString nVarchar(255)\n)\nRETURNS int\nas begin\nDeclare @PseudoMatrix table \n\u00a0\u00a0\u00a0\u00a0\u00a0(location int identity primary key, \n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0firstorder int not null, \n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Firstch nchar(1), \n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0secondorder int not null, \n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Secondch nchar(1), \n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Thevalue int not null default 0,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0PreviousRowValues varchar(200)\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0)\n\ninsert into @PseudoMatrix (firstorder, firstch, secondorder, secondch, TheValue )\nSELECT TheFirst.number,TheFirst.ch, TheSecond.number,TheSecond.ch,0\n\u00a0 FROM --divide up the first string into a table of characters\/sequence\n\u00a0\u00a0 (SELECT number, SUBSTRING(@FirstString,number,1) AS ch\n\u00a0\u00a0\u00a0 FROM numbers WHERE number &lt;= LEN(@FirstString) union all Select 0,Char(0)) TheFirst \n\u00a0 cross JOIN --divide up the second string into a table of characters\/sequence\n\u00a0\u00a0 (SELECT number, SUBSTRING(@SecondString,number,1) AS ch\n\u00a0\u00a0\u00a0 FROM numbers WHERE number &lt;= LEN(@SecondString) union all Select 0,Char(0)) TheSecond\n\u00a0 --ON Thefirst.ch= Thesecond.ch --do all valid matches\norder by TheFirst.number, TheSecond.number \n\nDeclare @current Varchar(255)\nDeclare @previous Varchar(255)\nDeclare @TheValue int\nDeclare @Deletion int, @Insertion int, @Substitution int, @minim int\nSelect @current='', @previous=''\nUpdate @PseudoMatrix\n\u00a0\u00a0\u00a0 Set\n\u00a0\u00a0\u00a0 @Deletion=@TheValue+1,\n\u00a0\u00a0\u00a0 @Insertion=ascii(substring(@previous,secondorder+1,1))+1,\n\u00a0\u00a0\u00a0 @Substitution=ascii(substring(@previous,(secondorder),1)) +1,\n\u00a0\u00a0\u00a0 @minim=case when @Deletion&lt;@Insertion then @Deletion else @insertion end,\n\u00a0\u00a0\u00a0 @TheValue = Thevalue = case --when Firstorder+SecondOrder=0 then 0\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 when SecondOrder=0 then FirstOrder\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 When FirstOrder=0 then Secondorder\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 when FirstCh=SecondCh then ascii(substring(@previous,(secondorder),1))\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 else case when @Minim&lt;@Substitution then @Minim else @Substitution end\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 end,\n\u00a0\u00a0\u00a0 @Previous=PreviousRowValues=case when secondorder =0 then @current else @Previous end,\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\n\u00a0\u00a0\u00a0 @current= case when secondorder =0 then char(@TheValue) else @Current+char(@TheValue) end\u00a0\u00a0\u00a0 \nreturn @TheValue\nEnd\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \nGo\n<\/pre>\n<p>What to use? If I need the performance, and I&#8217;m allowed CLR, I use the CLR approach. Otherwise, I still go for the &#8216;Arnold Fribble&#8217; approach to comparing strings.<\/p>\n<p><strong>You may also be interested in:<\/strong> <a href=\"https:\/\/www.red-gate.com\/simple-talk\/featured\/using-regex-in-sql-server-2025-complete-guide\/\" target=\"_blank\" rel=\"noopener\">How to use regex in SQL Server 2025 for pattern-based string matching<\/a><\/p>\n<p>It scales reasonably well and is reasonably fast, but I can&#8217;t help thinking that with window functions and number table, it would be possible to get an &#8216;edit distance&#8217; figure\u00a0 efficiently from a routine that is genuinely relational.<\/p>\n<p><strong>You may also be interested in:<\/strong> <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/fuzzy-searches-sql-server\/\" target=\"_blank\" rel=\"noopener\">Fuzzy string search techniques in SQL Server<\/a><\/p>\n<\/div>\n\n\n\n<section id=\"faq\" class=\"faq-block my-5xl\">\n    <h2>FAQs: String Comparisons in SQL: Edit Distance and the Levenshtein algorithm<\/h2>\n\n                        <h3 class=\"mt-4xl\">1. What is Levenshtein distance and how does it work in SQL Server?<\/h3>\n            <div class=\"faq-answer\">\n                <p>Levenshtein distance is the minimum number of single-character edits &#8211; insertions, deletions, or substitutions &#8211; required to change one string into another. A distance of 0 means the strings are identical; a distance of 3 means three edits are needed. In SQL Server, you calculate it using either a T-SQL implementation (Arnold Fribble&#8217;s string-based approach) or a CLR assembly that wraps .NET&#8217;s string comparison library. Neither is built into SQL Server natively.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">2. What is the fastest way to calculate Levenshtein distance in SQL Server?<\/h3>\n            <div class=\"faq-answer\">\n                <p>For production use with large datasets, the CLR approach is faster &#8211; it runs compiled .NET code rather than interpreted T-SQL and supports the Damerau-Levenshtein variant which handles transpositions as well as insertions, deletions, and substitutions. For smaller datasets or environments where CLR is not available, Arnold Fribble&#8217;s T-SQL implementation is the best pure-SQL option and performs acceptably for most use cases.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">3. What is the difference between Levenshtein and Damerau-Levenshtein distance?<\/h3>\n            <div class=\"faq-answer\">\n                <p>Standard Levenshtein distance counts three edit operations: insertions, deletions, and substitutions. Damerau-Levenshtein adds a fourth: transpositions (swapping two adjacent characters). This makes it more accurate for detecting common typing errors like &#8216;teh&#8217; instead of &#8216;the&#8217;. For most string similarity tasks in SQL Server, the Damerau-Levenshtein variant is preferable when using CLR, as it catches a broader range of human input errors.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">4. Can I use SQL Server string similarity functions for fuzzy search?<\/h3>\n            <div class=\"faq-answer\">\n                <p>Yes, but Levenshtein distance is typically one component of a broader fuzzy search strategy rather than a standalone solution. For high-volume fuzzy search, combine edit distance filtering with indexed full-text search (FTS) to pre-filter candidates before computing exact distances. Alternatively, the Fuzzy Searches article on Simple Talk covers SQL Server SOUNDEX, DIFFERENCE, and trigram-based approaches that may be more appropriate for phonetic similarity rather than character-level edit distance.<\/p>\n            <\/div>\n            <\/section>\n","protected":false},"excerpt":{"rendered":"<p>Calculate string similarity in SQL Server using edit distance and the Levenshtein algorithm. Covers Arnold Fribble&#8217;s T-SQL implementation, a CLR-based Damerau-Levenshtein solution, and practical guidance on which approach to use for performance-sensitive queries.&hellip;<\/p>\n","protected":false},"author":154613,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2],"tags":[],"coauthors":[6813],"class_list":["post-7689","post","type-post","status-publish","format-standard","hentry","category-blogs"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/7689","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/154613"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=7689"}],"version-history":[{"count":16,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/7689\/revisions"}],"predecessor-version":[{"id":110027,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/7689\/revisions\/110027"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=7689"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=7689"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=7689"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=7689"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}