SQL WHERE 语句,其中字符串可以相差最多一个字符,但其他方面相等

SQL WHERE statement where string can differ maximum one char but otherwise be equal

提问人:ArturM 提问时间:9/21/2023 更新时间:9/21/2023 访问量:85

问:

我正在尝试创建一个 SQL WHERE 语句来比较字符串,它允许的最大一个字符不同,但仍然必须具有相同的长度。我尝试过使用带有 LIKE 命令的子字符串搜索,但如果字符串中间的字符不同等,它就不能很好地工作。应与示例匹配的字符串:

应匹配: “ABC123”和“ABC023” “ABC124”和“ABC125”等

不应匹配

“ABC123”和“ACB132”

如何使用 SQL WHERE 语句实现这一点?

sql-server 字符串 字符串搜索

评论

0赞 Panagiotis Kanavos 9/21/2023
没有便宜的方法。无论您最终使用什么表达式,它都无法使用任何索引。有多少数据?您可以通过在 Python 脚本中处理数据并计算字符串之间的“距离”(字符差异)来执行您想要的操作。如果使用 SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017SQL Server 2017

答:

4赞 Thom A 9/21/2023 #1

假设你使用的是最新版本的 SQL Server(在撰写本文时为 2022 年),则可以用于为搜索字符串中的每个字符获取一行,然后使用单个字符通配符 () 替换位置中的每个字符。然后你可以在里面使用一个:GENERATE_SERIESSTUFF_LIKEEXISTS

DECLARE @YourString varchar(6) = 'ABC123';

SELECT YT.YourColumn
FROM dbo.YourTable YT
WHERE EXISTS(SELECT 1
             FROM GENERATE_SERIES(1,LEN(@YourString)) GS
             WHERE YT.YourColumn LIKE STUFF(@YourString,GS.value,1,'_'));

db<>小提琴

如果不在 SQL Server 2022+ 中,则可以使用自己的计数函数来实现相同的结果。

2赞 Isolated 9/21/2023 #2

您可以创建一个 levenshtein 距离函数,然后在 WHERE 子句中调用该函数。

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
create table table1 (
  id integer, 
  string1 varchar(50), 
  string2 varchar(50)
  );
insert into table1 values 
(1, 'ABC123', 'ABC023'),
(2, 'ABC123', 'ABC12'),
(3, 'ABC124', 'ABC125'),
(4, 'ABC123', 'ABC132');
select *
  from table1
 where dbo.edit_distance(string1,string2) <= 1
   and len(string1) = len(string2)
编号 字符串 1 字符串2
1 ABC123系列 ABC023系列
3 ABC124系列 ABC125型

小提琴

评论

1赞 siggemannen 9/22/2023
我没有投反对票,但我猜莱文斯坦函数对于这项任务来说有点矫枉过正(而且很慢)。你不需要整个shebang,可以更早地跳出来,因为diff > 1并不有趣