【问题标题】:Match two columns if all the words in one are contained in the other如果一列中的所有单词都包含在另一列中,则匹配两列
【发布时间】:2020-01-12 06:40:58
【问题描述】:

我正在尝试将 2 列(在 2 个单独的表中)链接在一起,以便如果一列中的每个单词都包含在另一列中,那么它们将匹配。

例如,以下值应匹配:

Paul Smith|Paul Andrew Smith
Paul Smith|Paul Andrew William Smith
Paul William Smith|Paul Andrew William Smith
Paul Andrew Smith|Paul Smith

但不应匹配以下内容:

Paul William Smith|Paul Andrew Smith

我使用的是 SQL Server 2016。

我想通过SELECT 查询来做到这一点。我对使用 string_split 函数(在空格上)有一个模糊的想法,交叉应用 2 个表,然后使用 MAX 函数,但是如果我只处理几千个名称,这将创建数百万行,所以它效率不会很高。

样本数据:

DROP TABLE IF EXISTS #TEMP1
DROP TABLE IF EXISTS #TEMP2

CREATE TABLE #TEMP1 (NAME NVARCHAR(300))
CREATE TABLE #TEMP2 (NAME NVARCHAR(300))

INSERT #TEMP1 SELECT 'Paul Smith'
INSERT #TEMP1 SELECT 'Amy Nicholas Stanton'
INSERT #TEMP1 SELECT 'Andrew James Thomas'

INSERT #TEMP2 SELECT 'Paul Andrew Smith'
INSERT #TEMP2 SELECT 'Amy Stanton'
INSERT #TEMP2 SELECT 'Andrew Marcus Thomas'

所以从样本数据来看,前 2 行应该匹配,3 行不应该匹配。

编辑:我已经将我模糊的想法付诸实践,以下解决方案有效,但正如我所料,当您处理包含数千行的表时,它真的很慢。

SELECT DISTINCT A.[FIRSTNAME],A.[SECONDNAME]
FROM (
    SELECT *
          ,MIN([FIRSTMATCH]) OVER(PARTITION BY [SRN],[FIRSTNAME]) [FM]
          ,MIN([SECONDMATCH]) OVER(PARTITION BY [FRN],[SECONDNAME]) [SM]
    FROM (
            SELECT  DISTINCT A.NAME [FIRSTNAME]
                            ,B.NAME [SECONDNAME]
                            ,A.value [FIRSTVAL]
                            ,MAX(IIF(A.VALUE=B.VALUE,1,0)) OVER(PARTITION BY A.VALUE,B.RN) [FIRSTMATCH]
                            ,B.value [SECONDVAL]
                            ,MAX(IIF(B.VALUE=A.VALUE,1,0)) OVER(PARTITION BY B.VALUE,A.RN)  [SECONDMATCH]
                            ,A.RN [FRN]
                            ,B.RN [SRN]
            FROM (
                    SELECT DISTINCT NAME, DENSE_RANK() OVER(ORDER BY NAME) [RN],value
                    FROM #TEMP1
                    CROSS APPLY STRING_SPLIT(LTRIM(RTRIM(NAME)),' ')
                    WHERE LTRIM(RTRIM(NAME)) !=''
            )A
            CROSS APPLY(
                    SELECT DISTINCT NAME, DENSE_RANK() OVER(ORDER BY NAME) [RN],value
                    FROM #TEMP2
                    CROSS APPLY STRING_SPLIT(LTRIM(RTRIM(NAME)),' ')
                    WHERE LTRIM(RTRIM(NAME)) !=''
            )B 
    )A
)A
WHERE A.SM = 1 OR A.FM = 1

【问题讨论】:

  • 要求您包含 DDL、示例数据和预期输出,然后逻辑将清晰且易于任何人回答。
  • 我的例子算不算样本数据?
  • 如果能把当前数据分叉/分离成两张表和预期输出的形式就更好了。
  • 我已添加示例数据
  • 它让我想起了我遇到的类似问题,我为此使用了全文索引。我知道它不受欢迎,但我只是想把这个想法扔出去,如果你愿意尝试的话。这是我过去发布的几个问题,以及我得到的解决方案123

标签: sql sql-server fuzzy-logic


【解决方案1】:

您可以拆分字符串并聚合。假设所有名称都没有重复部分:

with n1 as (
      select temp1.name, value as part, count(value) over (partition by name) as num_parts
      from temp1 cross apply
           string_split(temp1.name, ' ')
     ),
     n2 as (
      select temp2.name, value as part, count(value) over (partition by name) as num_parts
      from temp2 cross apply
           string_split(temp2.name, ' ') 
     )
select n1.name, n2.name
from n1 join
     n2
     on n1.part = n2.part and n1.num_parts <= n2.num_parts
group by n1.name, n2.name, n1.num_parts
having count(*) = n1.num_parts;

Here 是一个 dbfiddle。

【讨论】:

  • 谢谢,这是一个好的开始,但不幸的是有点限制。这仅适用于 temp 1 中的名称比 temp 2 中的名称少的行(例如,如果您将 Paul Smith 的值与 Paul Andrew Smith 交换,它将不起作用)。当然可以使用在连接条件和分组中交换的表运行两次,但我想知道是否有一种方法可以在一个查询中执行此操作。
【解决方案2】:

基于 Gordon Linoff 的回答,这似乎可行:

;WITH N1 AS (
      SELECT *,COUNT(*) OVER(PARTITION BY NAME) [NUM_PARTS]
      FROM (
            SELECT DISTINCT NAME, VALUE [PART]
            FROM #TEMP1 CROSS APPLY
                 STRING_SPLIT(#TEMP1.NAME, ' ')
           )A
     ),
     N2 AS (
      SELECT *,COUNT(*) OVER(PARTITION BY NAME) [NUM_PARTS]
      FROM (
            SELECT DISTINCT NAME, VALUE [PART]
            FROM #TEMP2 CROSS APPLY
           STRING_SPLIT(#TEMP2.NAME, ' ')
           )A 
     )
SELECT N1.NAME, N2.NAME
FROM N1 JOIN N2 ON N1.PART = N2.PART
group by n1.name, n2.name, n1.num_parts,n2.num_parts
having count(n2.part) = n1.num_parts
or     count(n1.part) = n2.num_parts

【讨论】:

    猜你喜欢
    • 2015-02-11
    • 1970-01-01
    • 2020-02-02
    • 2022-12-01
    • 1970-01-01
    • 2016-06-10
    • 1970-01-01
    • 2020-11-27
    • 1970-01-01
    相关资源
    最近更新 更多