【问题标题】:A very complicated SQL query issue一个非常复杂的 SQL 查询问题
【发布时间】:2011-11-04 19:35:34
【问题描述】:

我有两张桌子...

  • 客户
  • 客户识别

客户表有 2 个字段

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

CustomerIdentification 表有 3 个字段

  • CustomerId varchar(20)
  • Identification_Number varchar(50)
  • Personal_ID_Type_Code int -- 是另一个表的外键,但不相关

基本上,Customer 是客户主表(以 CustomerID 作为主键),并且 CustomerIdentification 可以有给定客户的多个标识。换句话说,CustomerIdentification 中的 CustomerId 是 Customer 表的外键。一个客户可以拥有许多身份证明,每个身份都有Identification_NumberPersonal_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

基本上,多个客户可以在CustomerIdentification 中拥有相同的Identification_NumberPersonal_ID_Type_Code。发生这种情况时,所有 Customer_Id_Link 字段都需要使用一个通用值(可以是 GUID 或其他)进行更新。但对此的处理更为复杂。

规则如下:

用于匹配客户记录之间的 Personal_ID_Type_CodeIdentification_Number 字段 - 比较上述匹配中所有客户记录的所有其他常见 Personal_ID_Type_Code 字段的 Identification_Number 字段 - 如果为真,则链接客户记录

例如:

为 CU-1、CU-2、CU-3、CU-4 匹配 ID 1 A

  • 异常 ID 2 不匹配(CU-1 上的 A 与 CU-3 上的 B)
  • 没有完成链接

为 CU-3、CU-4 匹配 ID 2 B

  • 没有 ID 不匹配
  • 链接 CU-3 和 CU-4(更新 Customer_Id_Link 字段,在客户表中为两者使用一个共同值)

为 CU-1、CU-4 匹配 ID 3 A

  • 异常 ID 2 不匹配(A 与 B)
  • 没有完成链接

为 CU-2、CU-5 匹配 ID 3 B

  • 没有 ID 不匹配
  • 链接 CU-2 和 CU-5(更新 Customer_Id_Link 字段,为两者在客户表中使用公共值)匹配 CU-2、CU-3 的 ID 4 C
  • CU-2 已链接,将 CU-5 保留到客户链接列表中
  • CU-3 已链接,请将 CU-4 保留到客户链接列表中
  • 异常 ID 3 不匹配(CU-2 上的 B 与 CU-4 上的 A)
  • 未完成链接(保留之前的链接)

任何帮助将不胜感激。这让我醒了两天,我似乎无法找到解决方案。理想情况下,解决方案将是一个存储过程,我可以执行它来进行客户链接。

- SQL Server 2008 R2 标准 64 位

更新-------------------

我知道解释这个问题会很困难,所以我承担了责任。但本质上,我希望能够链接所有具有相同标识号的客户,只有一个客户可以拥有多个标识号。以示例 1. 1 A(1 是 Personal_id_type_code 和 A 是 IdentificationNumber 存在于 4 个不同的客户。CU-1,CU-2,CU-3,CU-4。所以他们可能是同一个客户存在 4 个不同的时间具有不同客户ID的客户表。我们需要将它们与1个共同值联系起来。但是,CU-1还有2个其他标识,如果其中1个与其他3个不同(CU-2,CU-3,CU-4 ) 他们不是同一个客户。因此,带有 Num A 的 ID 2 与 CU-3(其 B)的 ID 2 不匹配,对于 CU-4 也是如此。此外,即使 ID 2 num A 在 CU-2 中不存在, CU-1 的 ID 3 和 num A 与 CU-2 的 ID 3(它的 B)不匹配。因此根本不匹配。

下一个常见的 Id 和 num 是 2-b,它存在于 CU-3 和 CU-4 中。这两个客户实际上是相同的,因为它们都有 ID 1 - A 和 ID 2 - B。ID 4 - C 和 ID 3 - A 无关紧要,因为两个 ID 不同。这实质上意味着该客户有 4 个 ID I A、2 B、4 C 和 3 A。所以现在我们需要将此客户与客户表中的一个共同的唯一值(guid)联系起来。

我希望我现在解释了这个非常复杂的问题。很难解释,因为这是一个非常独特的问题。

【问题讨论】:

  • 您的标准还不是很清楚...您能详细说明一下吗?
  • 您正在尝试确定哪些客户 ID 指的是单个客户?对于每个 Personal_ID_Type_Code 两个客户 ID 有共同点,如果他们的 Identification_Number 也匹配,这两个客户 ID 标识一个客户。您可以通过 Customer.Customer_Id_Link 中的值识别那些“匹配”的客户 ID。对吗?
  • 没错。除非,如果客户 ID 有 1 个 Personal_ID_Type_Code 和标识号相同,那么其他的也应该匹配或完全不同的 Personal_ID_Type_Code 才能成为同一客户。

标签: sql-server sql-server-2008 sql-server-2008-r2


【解决方案1】:

我已经稍微改变了你的数据模型,试图让它更明显地发生了什么..

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]

【讨论】:

  • 这是朝着正确方向迈出的一步,汤姆,仍未完全解决,但我们正在前进。不过我有一个问题,但首先,从两张桌子上除掉马特和丹(所以我们只剩下 5 位顾客),因为他们是不必要的。此外,将 ('Tom', 'B', 'Drivers Licence') 记录更新为 ('Tom', 'A', 'Drivers Licence')。现在,如果您将客户重命名为我最初提供的名称,为什么查询会返回不同的结果。例如:将 CU-1 用于 Fred,CU-2 用于 Bob,CU-3 用于 Vince,CU-4 用于 Tom 和 CU-5 用于 Alice,你会看到结果会有所不同,这很奇怪
  • 与您提供的实际人名不同。我认为两种情况下的结果应该仍然相同。没有??
  • 另外,有没有办法,而不是使用 HASHBYTES 我们可以使用 NewID??
  • 嗨,汤姆,你能帮我解答我的第一个问题吗?根据使用的名称,它会产生不同的输出。为什么??
  • 我认为 AND ci1.[CustomerName]
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多