忽略特定字母以在SQL查询中查找匹配项

时间:2014-01-23 17:56:34

标签: sql sql-server

我想在表中查询另一个表上列表中的所有值以查找匹配项,但我知道任何一个表中的某些值可能输入错误。一个表可能有'10Hf7K8'而另一个表可能有'1OHf7K8',但我仍然希望它们匹配。

另一个例子,如果一个表有'STOP',但我知道在myTable中,有些字段可能会说'5T0P'或'ST0P'或'5TOP'。我希望那些结果也是如此。如果我想要'ZEPT'和'2EPT'匹配,'2'和'Z'可能会出现同样的情况。

所以,如果我知道要解释'0'和'O','5'和'S'以及'Z'和'2'之间的不一致,并且知道它们将在同一个地方,但我做不知道它们在单词中的确切位置或单词将包含多少个字母,是否可以忽略这些字母进行查询?

附加信息:这些值是数百个串行密钥,我无法确认两个表之间的版本是否正确。我不应该在我的例子中使用实际的单词,这些值可以是任何顺序的字母和数字的任意组合。没有明显的模式我可以硬编码。

解决方案:Goat CO,Learning和user3216429的答案包含了我需要的解决方案。我能够在保留基础数据的同时找到匹配的值。

5 个答案:

答案 0 :(得分:0)

最好使用清理数据,但如果无法更改基础数据,则可以使用嵌套的REPLACE()语句:

SELECT *
FROM Table1 a
JOIN Table2 b
  ON REPLACE(REPLACE(REPLACE(a.field1,'2','Z'),'5','S'),'0','O') = REPLACE(REPLACE(REPLACE(b.field1,'2','Z'),'5','S'),'0','O')

清理数据可以是相同的嵌套替换语句:

ALTER TABLE Table1 ADD cleanfield VARCHAR(25)
UPDATE Table1
SET cleanfield = REPLACE(REPLACE(REPLACE(dirtyfield,'2','Z'),'5','S'),'0','O')

然后你就可以加入干净的领域了。

答案 1 :(得分:0)

您可以而且应该做的是清理您的数据,将所有这些2,0,5替换为Z,O and S

但是如果你想尝试其他解决方案,那么你可以尝试这样的事情

select case when 
REPLACE(REPLACE(REPLACE('stop','0','o'),'5','s'),'2','Z') = REPLACE(REPLACE(REPLACE('5t0p','0','o'),'5','s'),'2','Z') then 1 else 2 end

答案 2 :(得分:0)

如前所述,如果你有时间,请清理数据。

如果没有,SQL SERVER提供两个可能有用的字符串函数。

这个例子来自我的博客文章。 http://craftydba.com/?p=5211

SOUNDEX()函数将单词转换为4个字符的值。 DIFFERENCE()函数告诉你两个单词的接近程度。

你的例子似乎只有一个字。您可能希望使用计算列并对其进行索引,以使where子句具有SARGABLE。

如果您使用段落,请使用标准分割功能将文本段落转换为单词。使用这些功能搜索数据。但是,这将导致非SARGABLE表达。

-- Example returns 4, words are very close
select 
    soundex('Dog') as word_val1, 
    soundex('Dogs') as word_val2, 
    difference('Dog', 'Dogs') as how_close


-- Example returns 0, words are very different
select 
    soundex('Rattle-Snake') as word_val1, 
    soundex('Mongoose') as word_val2, 
    difference('Rattle-Snake', 'Mongoose') as how_close

output: 

word_val1 word_val2 how_close
--------- --------- -----------
D200      D200      4


word_val1 word_val2 how_close
--------- --------- -----------
R340      M522      0

最后但同样重要的是,您还可以查看全文索引的速度。这需要一些额外的开销(FTI结构和更新FTI的过程)。

http://craftydba.com/?p=1421

答案 3 :(得分:0)

  select REPLACE(REPLACE( REPLACE([column_name],'O','0'),'Z','2'),'5','S')
  from [table_name]

答案 4 :(得分:0)

1)要过滤掉包含所有形式的STOP字(STOP,5TOP,ST0P,5T0P)的所有行,您可以使用以下基于LIKE的查询:

SELECT  *
FROM (
    SELECT 1, 'CocoJambo' UNION ALL 
    SELECT 2, '5T0P' UNION ALL 
    SELECT 3, ' 5TOP ' UNION ALL 
    SELECT 4, '  ST0P ' UNION ALL 
    SELECT 5, '   STOP ' UNION ALL 
    SELECT 6, 'ZTOP'
) x (ID, ColA)
WHERE x.ColA LIKE '%[5S]T[0O]P%';

输出:

ID          ColA
----------- ---------
2           5T0P
3            5TOP 
4             ST0P 
5              STOP 

2)关于你的问题:

对于每张桌子

  • 首先,我会尝试为每个单词构建一个包含所有模式的表格,对于每个模式,我会存储正确/准确的单词,

  • 然后我会尝试用适当的单词替换每一个模式

在这两个表的预先之后,我将尝试匹配这两个表。

此脚本仅替换第一个出现的模式

SELECT x.*, oa.*, 
    CASE 
        WHEN oa.PatIx > 0 THEN STUFF( x.ColA , oa.PatIx , LEN(oa.Word), oa.Word )
        ELSE x.ColA
    END AS NewColA
FROM (
    SELECT 1, 'CocoJambo' UNION ALL 
    SELECT 2, '5T0P' UNION ALL 
    SELECT 3, ' 5TOP ' UNION ALL 
    SELECT 4, '  ST0P ' UNION ALL 
    SELECT 5, '   STOP jambo jumbo 5TOP bOb ' UNION ALL 
    SELECT 6, 'ZTOP'
) x (ID, ColA)
OUTER APPLY (
    SELECT  *
    FROM (
        SELECT  w.WordPattern, w.Word, PATINDEX( w.WordPattern , x.ColA ) AS PatIx
        FROM    @Words w 
    ) y
    WHERE y.PatIx > 0
) oa

输出:

ID ColA                          WordPattern  Word PatIx NewColA
-- ----------------------------- ------------ ---- ----- ----------------------------
1  CocoJambo                     %b[o0]%      bob  8     CocoJambob
2  5T0P                          %[5S]T[0O]P% STOP 1     STOP
3   5TOP                         %[5S]T[0O]P% STOP 2      STOP 
4    ST0P                        %[5S]T[0O]P% STOP 3       STOP 
5     STOP jambo jumbo 5TOP bOb  %[5S]T[0O]P% STOP 4        STOP jambo jumbo 5TOP bOb
5     STOP jambo jumbo 5TOP bOb  %b[o0]%      bob  12       STOP jambobjumbo 5TOP bOb
6  ZTOP                          NULL         NULL NULL  ZTOP

注意:这个解决方案只是一个概念验证。它需要发展。

或者您可以尝试使用正确格式替换所有错误单词的解决方案:

CREATE TABLE dbo.Words ( Id INT IDENTITY PRIMARY KEY, WordSource NVARCHAR(50) NOT NULL, Word NVARCHAR(50) NOT NULL );
INSERT dbo.Words ( WordSource , Word ) VALUES ( N'5T0P' , N'STOP' );
INSERT dbo.Words ( WordSource , Word ) VALUES ( N'5TOP' , N'STOP' );
INSERT dbo.Words ( WordSource , Word ) VALUES ( N'ST0P' , N'STOP' );
INSERT dbo.Words ( WordSource , Word ) VALUES ( N'b0b' , N'bob' );
INSERT dbo.Words ( WordSource , Word ) VALUES ( N'bOb' , N'bob' );
GO

CREATE FUNCTION dbo.ReplaceWords (@ColA NVARCHAR(4000), @Num INT)
RETURNS TABLE
AS
RETURN
WITH CteRecursive 
AS 
(
    SELECT  w.Id, w.WordSource, w.Word, REPLACE(@ColA, w.WordSource, w.Word) AS NewColA
    FROM    dbo.Words w
    WHERE   w.Id = 1

    UNION ALL 

    SELECT  w.Id, w.WordSource, w.Word, REPLACE(prev.NewColA, w.WordSource, w.Word) AS NewColA
    FROM    CteRecursive prev INNER JOIN dbo.Words w ON prev.Id + 1 = w.Id  
    WHERE   prev.Id + 1 <= @Num
)
SELECT  r.NewColA
FROM    CteRecursive r
WHERE   r.Id = @Num
GO

-- Testing 
SELECT * FROM dbo.ReplaceWords(N'   ST0P jambo 5TOP bOb jumbo ', 5) f;

输出

NewColA
----------------------------
   STOP jambo STOP bob jumbo

您可以使用以前的函数替换每个表中的所有错误单词,然后您可以比较两个表:

DECLARE @Num INT;
SET @Num = (SELECT COUNT(*) FROM  dbo.Words);

SELECT x.*, rpl.NewColA
FROM (
    SELECT 1, N'CocoJambo' UNION ALL 
    SELECT 2, N'5T0P' UNION ALL 
    SELECT 3, N' 5TOP ' UNION ALL 
    SELECT 4, N'  ST0P ' UNION ALL 
    SELECT 5, N'   STOP jambo jumbo 5TOP bOb ' UNION ALL 
    SELECT 6, N'ZTOP' UNION ALL 
    SELECT 7, N'' UNION ALL 
    SELECT 8, NULL
) x (ID, ColA)
OUTER APPLY dbo.ReplaceWords(x.ColA, @Num) rpl

输出:

ID ColA                          NewColA
-- ----------------------------- ----------------------------
1  CocoJambo                     CocoJambo
2  5T0P                          STOP
3   5TOP                          STOP 
4    ST0P                          STOP 
5     STOP jambo jumbo 5TOP bOb     STOP jambo jumbo STOP bob
6  ZTOP                          ZTOP
7                                
8  NULL                          NULL