You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SQL Server与Synapse SQL池Levenshtein函数返回结果不一致问题

问题根因

Synapse SQL池版本返回错误结果的核心原因是T-SQL WHILE循环的语法规则:如果循环体包含多条语句,必须用BEGIN/END包裹为语句块,否则只有紧邻WHILE的第一条语句会被当作循环体重复执行,后续语句会被当作循环外的代码仅执行一次。

你在把原SQL Server版本中WHILE后的单条多变量SELECT赋值拆为多条SET赋值时,后缀公共字符裁剪的WHILE循环没有加BEGIN/END块:

-- 错误写法
WHILE(@sLen > 0 AND SUBSTRING(@s, @sLen, 1) = SUBSTRING(@t, @tLen, 1))
    SET @sLen = @sLen - 1 -- 仅这条属于循环体,条件成立才执行
    SET @tLen = @tLen - 1 -- 不属于循环体!无论循环条件是否成立都会执行一次

对于测试用例select dbo.Levenshtein('aba','bab',10),两个字符串末尾字符a和b不匹配,循环条件不成立,@sLen保持3不变,但@tLen被错误减为2,导致后续距离数组初始化长度不足、字符对比范围缺失,最终返回错误结果1。
而select dbo.Levenshtein('aaa','bbb',10)能返回正确结果3,是因为该场景下@tLen被错误截断后的计算结果刚好和正确值重合,属于偶现的正确假象。

修复方案

给后缀公共字符裁剪的WHILE循环添加BEGIN/END块,将两个变量的SET赋值都纳入循环体即可,修复后的完整Synapse版本函数如下:

-- =============================================
-- 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).
-- http://blog.softwx.net/2014/12/optimizing-levenshtein-algorithm-in-tsql.html
-- 
-- 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
        SET @v0 = @s
        SET @i = @sLen -- temporarily use v0 for swap
        SET @s = @t
        SET @sLen = @tLen
        SET @t = @v0
        SET @tLen = @i
    END
    SET @max = ISNULL(@max, @tLen)
    SET @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))
    BEGIN
        SET @sLen = @sLen - 1
        SET @tLen = @tLen - 1
    END

    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)) 
        SET @start = @start + 1
    IF (@start > 1) BEGIN
        SET @sLen = @sLen - (@start - 1)
        SET @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

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

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

    SET @jOffset = @max - @lenDiff
    SET @i = 1
    WHILE (@i <= @sLen) BEGIN
        SET @distance = @i
        SET @diag = @i - 1
        SET @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
        SET @j = CASE WHEN @i <= @jOffset THEN 1 ELSE @i - @jOffset END
        SET @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)
            SET @left = UNICODE(SUBSTRING(@v0, @j, 1))
            SET @thisJ = @j
            SET @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
            SET @v0 = STUFF(@v0, @thisJ, 1, NCHAR(@distance))
            SET @diag = @left
            SET @j = case when (@distance > @max) AND (@thisJ = @i + @lenDiff) then @jEnd + 2 else @thisJ + 1 end
        END
        SET @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
GO

注:后续从SQL Server迁移T-SQL代码到Synapse SQL池时,只要将WHILE后的单条多变量赋值拆为多条语句,必须同步检查循环体是否添加了BEGIN/END块,避免同类逻辑错误。

内容的提问来源于stack exchange,提问作者nbot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 23:54:17