如何执行 Where 并通过相似的值查找

Posted

技术标签:

【中文标题】如何执行 Where 并通过相似的值查找【英文标题】:How to do a Where and find by similar values 【发布时间】:2020-09-03 10:16:25 【问题描述】:

我正在停车场工作,路障中装有摄像头,当这些摄像头看到一个车牌时,该牌照将作为一个动作在数据库中注册。

如果此许可证有加入停车场的许可,停车场将开放,但摄像头并不完美获得许可证,也许有时我会获得一些不同字符的许可证。

我的意思是,例如,如果许可证是 7631 BHC,我可能会得到一个错误的值,它可能是 1931 5HD。

我想知道的是 SQL 中是否有任何方法可以在选择中执行 WHERE 以查看许可证是否具有权限,以获取可能是相机返回的许可证。

例如,在我展示的情况下,有 3 个字符与可能的值 (3,1,H) 匹配。

因此,我在后台使用 C# 工作时,如果有这 3 个字符的巧合,我会说如果有超过 2 个巧合,障碍就会打开。

您知道任何返回可能许可证的选择方法吗?

我尝试了函数 Difference() 但此选项对我不起作用,因为总是返回 4(这意味着值非常相似)并且不是这种情况,因为具有值的许可证(9999 HHH,1234 ZZZ), by fare 不相似,除了字符串的结构。

【问题讨论】:

您想在 SQL 或 C# 中执行此操作? @Jamiec 在 SQL 中更好,但如果有办法在 C# 中进行此比较不是问题。 您可能想查看 Levenshtein 距离。但是,您的问题的真正答案是 SQL 没有内置的模糊匹配机制来满足您的特定要求。 【参考方案1】:

给定一个用户定义的函数:

CREATE FUNCTION dbo.CountMatches(@value NVARCHAR(100), @match NVARCHAR(100))
RETURNS INT AS
BEGIN
   DECLARE @i INT = 1;
   DECLARE @count INT = 0
   WHILE(@i < LEN(@match) AND @i<LEN(@value))
   BEGIN
      IF(SUBSTRING(@value,@i,1) = SUBSTRING(@match,@i,1))
      BEGIN
         SET @count = @count+1
      END
      SET @i = @i+1
   END
   RETURN @count
END

您可以将其直接插入 where 子句:

declare @camera nvarchar(100) = '19315HD'
SELECT plate
FROM Plates
WHERE dbo.CountMatches(plate,@camera) >= 3

或者,你希望结果和 where 子句中的匹配计数使用 CTE 来保存调用该方法两次

declare @camera nvarchar(100) = '19315HD'
;with data
as
(
    SELECT plate, dbo.CountMatches(plate, @camera) AS matchCount
    FROM Plates
)
select plate,matchCount
from data
where matchCount>=3

现场示例:https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=e55ae3788616ce14c3888db19d4aa865

【讨论】:

【参考方案2】:

如果您的输入数据和源数据将始终匹配模式 XXXX XXX(8 个字符,第 5 个是空格),那么您可以对此进行硬编码...

样本数据

declare @input nvarchar(8) = '1931 5HD';

declare @plates table
(
    plate nvarchar(8)
)
insert into @plates (plate) values
('1235 ZZZ'), -- 0 matches
('7631 BHC'), -- 3 matches
('1931 5HC'); -- 6 matches

解决方案

-- version 1
select  p.plate
from @plates p
where   case when substring(p.plate, 1, 1) = substring(@input, 1, 1) then 1 else 0 end
      + case when substring(p.plate, 2, 1) = substring(@input, 2, 1) then 1 else 0 end
      + case when substring(p.plate, 3, 1) = substring(@input, 3, 1) then 1 else 0 end
      + case when substring(p.plate, 4, 1) = substring(@input, 4, 1) then 1 else 0 end
      + case when substring(p.plate, 6, 1) = substring(@input, 6, 1) then 1 else 0 end
      + case when substring(p.plate, 7, 1) = substring(@input, 7, 1) then 1 else 0 end
      + case when substring(p.plate, 8, 1) = substring(@input, 8, 1) then 1 else 0 end > 2;

如果您想要匹配字符的计数,则可以在字段列表中复制 where 子句计算。这样,您甚至可以在按匹配字符数降序排序时仅选择 top 1 结果,从而为您提供最准确的车牌匹配。

-- version 2
select  top 1
        p.plate,
        case when substring(p.plate, 1, 1) = substring(@input, 1, 1) then 1 else 0 end
      + case when substring(p.plate, 2, 1) = substring(@input, 2, 1) then 1 else 0 end
      + case when substring(p.plate, 3, 1) = substring(@input, 3, 1) then 1 else 0 end
      + case when substring(p.plate, 4, 1) = substring(@input, 4, 1) then 1 else 0 end
      + case when substring(p.plate, 6, 1) = substring(@input, 6, 1) then 1 else 0 end
      + case when substring(p.plate, 7, 1) = substring(@input, 7, 1) then 1 else 0 end
      + case when substring(p.plate, 8, 1) = substring(@input, 8, 1) then 1 else 0 end as MatchCount
from @plates p
where   case when substring(p.plate, 1, 1) = substring(@input, 1, 1) then 1 else 0 end
      + case when substring(p.plate, 2, 1) = substring(@input, 2, 1) then 1 else 0 end
      + case when substring(p.plate, 3, 1) = substring(@input, 3, 1) then 1 else 0 end
      + case when substring(p.plate, 4, 1) = substring(@input, 4, 1) then 1 else 0 end
      + case when substring(p.plate, 6, 1) = substring(@input, 6, 1) then 1 else 0 end
      + case when substring(p.plate, 7, 1) = substring(@input, 7, 1) then 1 else 0 end
      + case when substring(p.plate, 8, 1) = substring(@input, 8, 1) then 1 else 0 end > 2
order by MatchCount desc;

另一个版本使用公用表表达式 (cte) 来避免一些重复代码。灵感来自Jamiec's comment。

-- version 3
with cte as
(
    select  p.plate,
            case when substring(p.plate, 1, 1) = substring(@input, 1, 1) then 1 else 0 end
          + case when substring(p.plate, 2, 1) = substring(@input, 2, 1) then 1 else 0 end
          + case when substring(p.plate, 3, 1) = substring(@input, 3, 1) then 1 else 0 end
          + case when substring(p.plate, 4, 1) = substring(@input, 4, 1) then 1 else 0 end
          + case when substring(p.plate, 6, 1) = substring(@input, 6, 1) then 1 else 0 end
          + case when substring(p.plate, 7, 1) = substring(@input, 7, 1) then 1 else 0 end
          + case when substring(p.plate, 8, 1) = substring(@input, 8, 1) then 1 else 0 end as MatchCount
    from @plates p
)
select top 1 plate, MatchCount
from cte
where MatchCount > 2
order by MatchCount desc;

结果

-- version 1
plate
--------
7631 BHC
1931 5HC

-- version 2 & 3
plate    MatchCount
-------- -----------
1931 5HC 6

【讨论】:

您可以使用 CTE,这样您就不必重复那个大而长的表达式两次。

以上是关于如何执行 Where 并通过相似的值查找的主要内容,如果未能解决你的问题,请参考以下文章

如何在Mac计算机上轻松查找和删除类似照片

如何从海量数据中找到相似数据--那些用于查找相似数据的哈希算法

在 Python 中查找最近的值并返回数组的索引

MySql中如何在一个字段(值为字符串)中查找某一个字符

如何通过linqdatasource的where筛选日期在某时间段的记录?

在Rails 4.1中,如何通过枚举符号查找记录?