Совпадение двух столбцов, если все слова в одном содержатся в другом

Я пытаюсь связать 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 (в пробелах), перекрестном применении двух таблиц, а затем использовании функции 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

person Anonymous    schedule 12.01.2020    source источник
comment
Попросите вас включить DDL, образцы данных и ожидаемый результат, тогда логика будет ясной и понятной для всех.   -  person Suraj Kumar    schedule 12.01.2020
comment
разве мой пример не считается образцом данных?   -  person Anonymous    schedule 12.01.2020
comment
Будет лучше, если вы сможете разделить/разделить свои текущие данные в виде двух таблиц и ожидаемого результата.   -  person Suraj Kumar    schedule 12.01.2020
comment
Я добавил образцы данных   -  person Anonymous    schedule 12.01.2020
comment
Это напоминает мне о похожей проблеме, с которой я столкнулся, и я использовал для нее полнотекстовое индексирование. Я знаю, что это неодобрительно, но я просто хотел высказать эту идею, если вы готовы попробовать. Вот несколько вопросов, которые я публиковал в прошлом по этому поводу, и решения, которые я получил 1 2 3   -  person Radu Gheorghiu    schedule 12.01.2020


Ответы (2)


Вы можете разделить строку и агрегировать. Предполагая, что ни одно из имен не имеет повторяющихся частей:

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;

Вот скрипт db‹>.

person Gordon Linoff    schedule 12.01.2020
comment
Спасибо, это хорошее начало, но, к сожалению, немного ограничительное. Это работает только для строк, в которых имена в темпе 1 содержат меньше терминов, чем имена в темпе 2 (например, если вы поменяете значения Пола Смита на Пола Эндрю Смита, это не сработает). Конечно, можно запустить его дважды, поменяв местами таблицы в условиях соединения и группировке, но мне интересно, есть ли способ сделать это в одном запросе. - person Anonymous; 13.01.2020

Основываясь на ответе Гордона Линоффа, похоже, это работает:

;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
person Anonymous    schedule 13.01.2020