Очень сложная проблема SQL-запроса - PullRequest
3 голосов
/ 04 ноября 2011

У меня есть 2 таблицы ...

  • Клиент
  • CustomerIdentification

Таблица клиентов имеет 2 поля

  • CustomerId varchar (20)
  • Customer_Id_Link varchar (50)

Таблица идентификаторов клиентов имеет 3 поля

  • CustomerId varchar (20)
  • Identification_Number varchar (50)
  • Personal_ID_Type_Code int - это внешний ключ к другой таблице, но это не имеет значения

По сути, Customer является главной таблицей клиента (с CustomerID в качестве первичного ключа) иCustomerIdentification может иметь несколько частей идентификации для данного клиента.Другими словами, CustomerId в CustomerIdentification является внешним ключом к таблице Customer.У клиента может быть много частей идентификации, каждая из которых имеет Identification_Number и Personal_ID_Type_Code (что является целым числом, указывающим, является ли идентификация паспортом, грехом, водительскими правами и т. Д.).

Теперь таблица клиента содержит следующие данные: Customer_Id_Link на данный момент пуста (пустая строка)

CustomerId      Customer_Id_Link
--------------------------------
 'CU-1'         <Blank>
 'CU-2'         <Blank>
 'CU-3'         <Blank>
 'CU-4'         <Blank>
 'CU-5'         <Blank>

, а таблица CustomerIdentification содержит следующие данные:

CustomerId    Identification_Number    Personal_ID_Type_Code
------------------------------------------------------------
'CU-1'        'A'                      1
'CU-1'        'A'                      2
'CU-1'        'A'                      3
'CU-2'        'A'                      1
'CU-2'        'B'                      3
'CU-2'        'C'                      4
'CU-3'        'A'                      1
'CU-3'        'B'                      2
'CU-3'        'C'                      4
'CU-4'        'A'                      1
'CU-4'        'B'                      2
'CU-4'        'B'                      3
'CU-5'        'B'                      3

По сути, несколько клиентов могут иметь одинаковые Identification_Number и Personal_ID_Type_Code в CustomerIdentification.Когда это происходит, все поля Customer_Id_Link необходимо обновить общим значением (может быть GUID или что-то еще).Но обработка для этого более сложна.

Правила таковы:

Для сопоставления полей Personal_ID_Type_Code и Identification_Number между записями клиентов - Сравните поля Identification_Number для всех других общих полей Personal_ID_Type_Code для всех записей клиентов изуказанное выше совпадение - если оно истинно, то свяжите записи клиентов

Например:

совпадение ID 1 A для CU-1, CU-2, CU-3,CU-4

  • Несовпадение идентификатора исключения 2 (A на CU-1 и B на CU-3)
  • Нет связи

Match ID 2B для CU-3, CU-4

  • Нет несоответствия идентификатора
  • Связь CU-3 и CU-4 (обновить поле Customer_Id_Link с общим значением в таблице клиентов для обоих)

Матч ID 3 A для CU-1, CU-4

  • Несоответствие ID 2 исключения (A против B)
  • Нет связи

Матч ID 3 B для CU-2, CU-5

  • Нет несоответствия идентификатора
  • Связь CU-2 и CU-5 (обновление Customer_Id_Link поле с общим значением в таблице клиента для обоих) Идентификатор совпадения 4 C для CU-2, CU-3
  • CU-2 уже связан, оставьтеCU-5 к списку ссылок клиентов
  • CU-3 уже подключен, оставьте CU-4 в списке ссылок клиентов
  • Несовпадение идентификатора исключения 3 (B на CU-2 и A на CU-4)
  • Нет связи (предыдущая связь остается)

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

- SQL Server 2008 R2 Standard 64 bit

ОБНОВЛЕНИЕ -------------------------------

Я знал, что будет сложно объяснить эту проблему, поэтому я беру на себя вину,Но, по сути, я хочу иметь возможность связать всех клиентов, имеющих одинаковые идентификационные номера, только у клиента может быть более 1 идентификационного номера.Возьмем пример 1. 1 A (1 - это Personal_id_type_code, а A - идентификационный номер существует для 4 разных клиентов. CU-1, CU-2, CU-3, CU-4. Таким образом, они могут быть одним и тем же клиентом, который существует 4 разных времени втаблица клиентов с другим идентификатором клиента. Нам нужно связать их с 1 общим значением. Однако CU-1 имеет 2 других идентификатора, и если хотя бы один из них отличается от других 3 (CU-2, CU-3, CU-4) они не являются одним и тем же клиентом. Таким образом, идентификатор 2 с номером A не совпадает с идентификатором 2 для CU-3 (его B) и одинаковым для CU-4. Кроме того, даже если идентификатор 2 с номером A не существует в CU-2ID 3 и номер A CU-1 не совпадают с ID 3 CU-2 (его B), поэтому не совпадают вообще.

Следующим общим идентификатором и номером является 2-b, который существует в CU-3 и CU-4. Эти два клиента фактически одинаковы, потому что оба имеют ID 1-A и ID 2-B. ID 4-C и ID 3-A не имеют значения, поскольку оба идентификатора различны. Что по сути означает, что у этого клиента есть 4 идентификатора I A, 2 B, 4 C и 3 A. Поэтому теперь нам нужно связать этого клиента с общим уникальным значением (guid) в таблице клиентов.

Надеюсь, я объяснил этот очень сложный вопрос сейчас. Это сложно объяснить, так как это очень уникальная проблема.

1 Ответ

2 голосов
/ 05 ноября 2011

Я немного изменил вашу модель данных, чтобы попытаться сделать ее более понятной.

CREATE TABLE [dbo].[Customer]
(
    [CustomerName]      VARCHAR(20)     NOT NULL,
    [CustomerLink]      VARBINARY(20)   NULL
)

CREATE TABLE [dbo].[CustomerIdentification]
(
    [CustomerName]      VARCHAR(20)     NOT NULL,
    [ID]                VARCHAR(50)     NOT NULL,
    [IDType]            VARCHAR(16)     NOT NULL
)

И я добавил еще несколько тестовых данных ..

INSERT  [dbo].[Customer]
        ([CustomerName])
VALUES  ('Fred'),
        ('Bob'),
        ('Vince'),
        ('Tom'),
        ('Alice'),
        ('Matt'),
        ('Dan')

INSERT  [dbo].[CustomerIdentification]
VALUES  
        ('Fred',    'A',    'Passport'),
        ('Fred',    'A',    'SIN'),
        ('Fred',    'A',    'Drivers Licence'),
        ('Bob',     'A',    'Passport'),
        ('Bob',     'B',    'Drivers Licence'),
        ('Bob',     'C',    'Credit Card'),
        ('Vince',   'A',    'Passport'),
        ('Vince',   'B',    'SIN'),
        ('Vince',   'C',    'Credit Card'),
        ('Tom',     'A',    'Passport'),
        ('Tom',     'B',    'SIN'),
        ('Tom',     'B',    'Drivers Licence'),
        ('Alice',   'B',    'Drivers Licence'),
        ('Matt',    'X',    'Drivers Licence'),
        ('Dan',     'X',    'Drivers Licence')

Это то, что вы ищете:

;WITH [cteNonMatchingIDs] AS (
    -- Pairs where the IDType is the same, but 
    -- name and ID don't match
    SELECT  ci3.[CustomerName] AS [CustomerName1],
            ci4.[CustomerName] AS [CustomerName2]
    FROM [dbo].[CustomerIdentification] ci3
    INNER JOIN [dbo].[CustomerIdentification] ci4
        ON ci3.[IDType] = ci4.[IDType]
    WHERE ci3.[CustomerName] <> ci4.[CustomerName]
    AND ci3.[ID] <> ci4.[ID]
),
[cteMatchedPairs] AS (
    -- Pairs where the IDType and ID match, and
    -- there aren't any non matching IDs for the
    -- CustomerName
    SELECT DISTINCT 
            ci1.[CustomerName] AS [CustomerName1],
            ci2.[CustomerName] AS [CustomerName2]
    FROM [dbo].[CustomerIdentification] ci1
    LEFT JOIN [dbo].[CustomerIdentification] ci2
        ON ci1.[CustomerName] <> ci2.[CustomerName]
        AND ci1.[IDType] = ci2.[IDType] 
    WHERE ci1.[ID] = ISNULL(ci2.[ID], ci1.[ID])
    AND NOT EXISTS (
        SELECT 1
        FROM [cteNonMatchingIDs]
        WHERE ci1.[CustomerName] = [CustomerName1] -- correlated subquery
        AND ci2.[CustomerName] = [CustomerName2]
    )
    AND ci1.[CustomerName] < ci2.[CustomerName]
),
[cteMatchedList] ([CustomerName], [CustomerNameList]) AS (
    -- Turn the matched pairs into list of matching
    -- CustomerNames
    SELECT  [CustomerName1],
            [CustomerNameList]
    FROM (
        SELECT  [CustomerName1],
                CONVERT(VARCHAR(1000), '$'
                 + [CustomerName1] + '$'
                 + [CustomerName2]) AS [CustomerNameList]
        FROM [cteMatchedPairs]
        UNION ALL
        SELECT  [CustomerName2],
                CONVERT(VARCHAR(1000), '$'
                 + [CustomerName2]) AS [CustomerNameList]
        FROM [cteMatchedPairs]
    ) [cteMatchedPairs]
    UNION ALL
    SELECT  [cteMatchedList].[CustomerName],
            CONVERT(VARCHAR(1000),[CustomerNameList] + '$'
             + [cteMatchedPairs].[CustomerName2])
    FROM [cteMatchedList] -- recursive CTE
    INNER JOIN [cteMatchedPairs]
        ON RIGHT([cteMatchedList].[CustomerNameList],
         LEN([cteMatchedPairs].[CustomerName1])
        ) = [cteMatchedPairs].[CustomerName1]
),
[cteSubstringLists] AS (
    SELECT  r1.[CustomerName],
            r2.[CustomerNameList]
    FROM [cteMatchedList] r1
    INNER JOIN [cteMatchedList] r2
        ON r2.[CustomerNameList] LIKE '%' + r1.[CustomerNameList] + '%'
),
[cteCustomerLink] AS (
    SELECT DISTINCT 
            x1.[CustomerName],
            HASHBYTES('SHA1', x2.[CustomerNameList]) AS [CustomerLink]
    FROM (
        SELECT  [CustomerName],
                MAX(LEN([CustomerNameList])) AS [MAX LEN CustomerList]
        FROM [cteSubstringLists]
        GROUP BY [CustomerName]
    ) x1
    INNER JOIN (
        SELECT  [CustomerName],
                LEN([CustomerNameList]) AS [LEN CustomerList], 
                [CustomerNameList]
        FROM [cteSubstringLists]
    ) x2
        ON x1.[MAX LEN CustomerList] = x2.[LEN CustomerList]
        AND x1.[CustomerName] = x2.[CustomerName]
)
UPDATE  c
SET     [CustomerLink] = cl.[CustomerLink]
FROM [dbo].[Customer] c
INNER JOIN [cteCustomerLink] cl
    ON cl.[CustomerName] = c.[CustomerName]


SELECT *
FROM [dbo].[Customer]
...