匹配任意4个连续字符的子字符串

问题描述:

我正在寻找匹配来自两个不同表的字符串的连接表达式,这两个表都包含4个连续字符的相同子字符串。匹配任意4个连续字符的子字符串

例如,下面应符合:

String1 String2 
-------- ----------- 
xxjohnyy abcjohnabc  [common substring: "john"] 
xxjohnyy johnny   [common substring: "john"] 
birdsings ravenbird  [common substring: "bird"] 
singbird a singer  [common substring: "sing"] 
+1

不确切地确定你在这里想要什么。你只是比较同一行的值,意思是t1.row1和t2.row2?或者,您是否正在查看t1中的每一行并在t2中查找所有匹配的内容?另外,当你找到一场比赛时你想做什么?加入表格并添加一列指示什么4char字符串匹配?另外,如果有2个4字符匹配(即duoew39uoie和uoewiyuoie)会怎样? – DiscipleMichael

+1

两张桌子有多大?这将涉及具有非平凡联接条件的交叉联接。 –

+0

@DiscipleMichael我想要“查看t1中的每一行并在t2中查找所有匹配”。背景:客户端维护了两个凌乱的Excel表格,每个表格有5000条记录,这些记录将被清理并移入数据库。两个表都包含一个“项目描述”,这是匹配所需要的,但是只能使用一个带描述的子字符串(例如姓氏)。此SQL仅在清理和导入过程中使用。基本上,我会为了完成95%的工作而加入,其余部分将进行手动审查。 –

这个问题很相似,找到Longest Common Substring问题。你找到最长的公共子串,然后你选择共同字符串为4的那些。你一定会发现this linkthis link对你很有帮助。

+0

谢谢,这看起来像是进入正确的方向。我会尽量在明天提供更多反馈。 –

这是一个很好的练习。这是我使用Tally Table的尝试。

SQL Fiddle

;WITH E1(N) AS(
    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 
), 
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b), 
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b), 
E8(N) AS(SELECT 1 FROM E4 a CROSS JOIN E4 b), 
Tally(N) AS(
    SELECT TOP (
     SELECT 
      CASE 
       WHEN MAX(LEN(String1)) > MAX(LEN(String2)) THEN MAX(LEN(String1)) 
       ELSE MAX(LEN(String2)) 
      END 
     FROM TestTable 
    ) 
     ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) 
    FROM E8 
), 
CteTable AS(-- Added an ID to uniquely identify each row 
    SELECT *, Id = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM TestTable 
), 
CteSubStr1 AS(
    SELECT 
     ct.*, 
     substr = SUBSTRING(ct.String1, t.N, 4) 
    FROM CteTable ct 
    CROSS APPLY(
     SELECT N FROM Tally 
     WHERE N <= LEN(ct.String1) - 3 
    )t 
), 
CteSubStr2 AS(
    SELECT 
     ct.*, 
     substr = SUBSTRING(ct.String2, t.N, 4) 
    FROM CteTable ct 
    CROSS APPLY(
     SELECT N FROM Tally 
     WHERE N <= LEN(ct.String2) - 3 
    )t 
), 
CteCommon AS(
    SELECT * FROM CteSubStr1 c1 
    WHERE EXISTS(
     SELECT 1 FROM CteSubStr2 
     WHERE 
      Id = c1.Id 
      AND substr = c1.substr 
    ) 
) 
SELECT 
    String1, String2, substr 
FROM (
    SELECT *, RN = ROW_NUMBER() OVER(PARTITION BY Id ORDER BY LEN(substr) DESC) 
    FROM CteCommon 
)t 
WHERE RN = 1 

结果

| String1 | String2 | substr | 
|-----------|------------|--------| 
| xxjohnyy | abcjohnabc | john | 
| xxjohnyy |  johnny | john | 
| birdsings | ravenbird | bird | 
| singbird | a singer | sing | 

这部分寻找最长公共子。

SELECT 
    String1, String2, substr 
FROM (
    SELECT *, RN = ROW_NUMBER() OVER(PARTITION BY Id ORDER BY LEN(substr) DESC) 
    FROM CteCommon 
)t 
WHERE RN = 1 

要获得所有常见字符串,而不是使用:

SELECT * FROM CteCommon 
+0

嘿,非常感谢,看起来很有趣!不知道有什么像'用x(n)'...想想我需要研究这一段时间... –

+0

你是指'E1(n)'..如果是的话,简称为“公共表格表达式”或“CTE”。 –

+0

我知道CTE并将它们用于递归查询,但我不知道你可以使用括号'(n)' –

;with pos as(select 1 as p 
      union all 
      select p + 1 from pos where p < 100), 
     uni as(select *, row_number() over(order by (select null)) id from t) 
select t1.s1, t1.s2, ca.s 
from uni t1 
cross apply(select substring(t2.s2, p, 4) s 
      from uni t2 
      cross join pos 
      where t1.id = t2.id and 
        len(substring(t2.s2, p, 4)) = 4 and 
        t1.s1 like '%' + substring(t2.s2, p, 4) + '%')ca 

Fiddlee http://sqlfiddle.com/#!3/bd4dd/16

只要改变100你列的实际长度......

+0

谢谢!这是短而有希望的。那么,今天我将讨论所有这些答案。 –

+0

注意:我建议不要为此使用递归cte或'rCTE'。使用Tally表进行计数要快得多。请参阅此[**文章**](http://www.sqlservercentral.com/articles/T-SQL/74118/)以获取更多信息。 –

+0

@wewesthemenace,在这种特殊情况下,差异会很小。即使您的色谱柱长度为8000,反复次数与计数之差也是300毫升。它不像你每行松散300毫秒,你每声明300毫秒。 –