【问题标题】:Find rows where they are "similar" and in which columns查找它们“相似”的行以及哪些列
【发布时间】:2019-03-01 15:02:22
【问题描述】:

我对 SQL Server (2017) 很陌生,我有这种需求:
考虑一下这个记录:

╔═════════════╦═══════════════╦══════════════╦═══════╗
║   Surname   ║     Name      ║ Day of birth ║ City  ║
╠═════════════╬═══════════════╬══════════════╬═══════╣
║ Rivers Lara ║ Wanda Leticia ║ 07/04/1956   ║ Paris ║
╚═════════════╩═══════════════╩══════════════╩═══════╝

我要在下面的列表中找到所有匹配的记录突出显示匹配的类型:

╔═════════════╦═══════════════╦══════════════╦════════╗
║   Surname   ║     Name      ║ Day of birth ║  City  ║
╠═════════════╬═══════════════╬══════════════╬════════╣
║ Rivers Lara ║ Wanda Leticia ║ 07/04/1956   ║ London ║
║ Rivers      ║ Leticia       ║ 07/04/1956   ║ Rome   ║
║ Rivers      ║ Leticia       ║ 14/03/1995   ║ Rome   ║
║ Rivers Lara ║ Leticia       ║ 07/04/1956   ║ Paris  ║
║ Rivers Lara ║ Wanda Leticia ║ 08/07/1983   ║ Paris  ║
╚═════════════╩═══════════════╩══════════════╩════════╝

例如:
第一行匹配 Surname+Name+dayofbirth
第二名:部分姓氏+部分名字+出生日期
第三名:部分姓氏+部分名字
姓氏+部分姓名+出生日期+城市的第四位
等等……

考虑到目前我们有固定数量的可能匹配但将来可能会增加(可能会添加更多列,例如税号或其他),我们将不胜感激有关如何处理此类查询的任何想法。

【问题讨论】:

  • 您需要的确切输出是什么?我通过对第二张表执行多个LEFT JOINs 来完成类似的操作,每个标准一个。所以基本上你在没有匹配时以NULL 值结束,在有匹配时以值结束。然后,您可能希望使用 ISNULLCOALESCE 对这些结果执行另一个查询。
  • 输出应该像第二个表,有一个额外的列,类型匹配(es surname,name or surname,partialname,dayofbirth 等)。这只是输出的一个想法(如果需要可以更改)。目标是找到具有 1、2、3 或 4 个相同值的人(姓氏和名字总是在 AND 中,而其他列是可选的)
  • SimoneG,任何答案都有效吗?从那以后你就没有发表过评论。
  • 最后我选择了一个接近 JBJ 想法的解决方案,使用 PARSENAME 来拆分多个姓(或名字)值,而不使用 soundex(我们不仅有英文名字)。谢谢!

标签: tsql sql-server-2017


【解决方案1】:

假设表示层是 html 并且您可以在查询输出中使用一些 html,这是一个粗略的想法,尽管它的工作效率不高,也没有部分匹配,只有精确。要匹配部分,您需要使用 charindex() 或 patindex() 并使用 left() 或 right() 在 ' ' 上拆分,可能会很复杂。

左/右词的拆分就像,至少我仍然是这样拆分的。

--this is only an example on the convoluted nature of string manipulation.
declare @Surname varchar(128) = 'Rivers Lara';
select 
     rtrim(iif(charindex(' ',@Surname) = 0,@Surname,Left(@Surname, charindex(' ',@Surname)))) first_part_Surname
    ,ltrim(reverse(iif(charindex(' ',@Surname) = 0,reverse(@Surname),Left(reverse(@Surname), charindex(' ',reverse(@Surname)))))) last_part_Surname
declare @StartRed varchar(50) = '<span style="color: red;">'
       ,@StopRed varchar(50) = '</span>';
select 
     case when tm.Surname = tr.Surname then @StartRed + tr.Surname + @StopRed else tr.Surname end Surname
    ,case when tm.Name = tr.Name then @StartRed + tr.Name  + @StopRed else tr.Name end [Name]
    ,case when tm.[Day of Birth] = tr.[Day of Birth] then @StartRed + convert(varchar, tr.[Day of Birth], 1) + @StopRed end [Day of Birth]
    ,case when tm.City = tr.City then @StartRed + tr.City + @StopRed else tr.City end City
from TableMatch tm
inner join TableRecords tr on (tm.Surname = tr.Surname or tm.Name = tr.Name) 
    and (tm.[Day of Birth] = tr.[Day of Birth] or tm.City = tr.City)

-- requires either Surname or Name to match and at least 1 of the 2 others to match

此外,您可以使用soundex() 来查找听起来像其他名称的名称,作为权宜之计,无需任何操作。您还可以 Left() soundex() 值以获得更广泛的匹配,但如果您转到 left(soundex(name),1) 匹配,您最终会得到所有以第一个字母开头的名称。

【讨论】:

    【解决方案2】:

    除了 Andrew 的评论之外,您还可以使用单个自联接来处理它,该自联接对要检查的每一列都有 OR 链接条件:

    ON Col1=Col1
    OR Col2=Col2
    OR Col3=Col3
    etc...
    

    那么,您希望使用这种匹配类型的额外列将是一个大型 CASE 表达式,其中包含您希望在此列中看到的每种可能组合的 WHEN..THEN。

    WHEN Col1=Col1 and Col2<>Col2 and Col3<>Col3 THEN 'Col1'
    WHEN Col1=Col1 and Col2=Col2 and Col3<>Col3 THEN 'Col1, Col2'
    etc...
    

    在上面的示例中,我假设所有列都不能包含 NULL,但如果它们可以,您也必须在逻辑中处理它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-09-12
      • 2011-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-20
      • 1970-01-01
      • 2011-05-04
      相关资源
      最近更新 更多