一个复杂的sql问题

时间:2011-11-09 13:52:44

标签: sql

我有这两张桌子:

CREATE TABLE [dbo].[Customer]
(
    [CustomerName]  VARCHAR(20)  NOT NULL,
    [CustomerLink]  VARCHAR(40)  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')

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',    'A',  'Drivers Licence'),
        ('Alice',  'B',  'Drivers Licence')

基本上,客户(来自客户表)可以拥有许多标识。例如,弗雷德有护照,罪和驾驶执照,所有3的价值都是A(价值也可能不同)。

这是我想要的输出:

测试1:值为A的护照

从表中可以看出:第一个常见标识是Passport,其值为A:4客户拥有此ID。 Fred,Bob,Vince和Tom也许他们都是同一个客户,如果是这样的话,我们想在客户表的CustomerLink字段中将所有4个用唯一ID(guid)链接起来。

但是,如果客户之间有1个ID匹配,那么其他客户也应该匹配,如果其中任何一个具有其他常见IDType。例如,Fred也有一个值为A的SIN,它也存在于Vince和Tom上,但值为B.所以这个组不是同一个客户。没有完成链接。

测试2:SIN值为B

接下来的常见识别是对于Vince和Tom存在的值为B的SIN。文斯和汤姆确实是同一个客户,因为他们也拥有相同价值的Passport(A)。两者的第三个标识具有不同的IDType,信用卡和驾驶执照。因此他们可以联系起来。因此,我们将vince和Tom链接为客户表中的同一客户。

测试3:值为A的驾驶执照

弗雷德和汤姆的存在。两者都有价值A的护照。两者都具有SIN共同点,但是,SIN的值对于两者都是不同的。它为弗雷德和B为汤姆。因此,他们不是同一个客户。没有完成链接。

测试4:具有值B的驾驶执照

鲍勃和爱丽丝存在。他们确实是同一个客户,因为Bob和Alice都拥有价值B的驾驶执照.Bob有另外2个ID,alice没有,这很好,因为那些ID是Bob独有的。因此,我们将Bob和Alice作为客户表中的同一客户进行链接。

测试5:价值为C的信用卡

鲍勃和文斯的存在。

但鲍勃已经与爱丽丝联系了,所以我们必须把爱丽丝带到照片中。 文斯已经和汤姆联系了,所以我们必须把汤姆带到画面。

现在,Bob拥有值为B的驾驶执照,而Tom也拥有值为A的驾驶执照。不同的值但相同的ID(驾驶执照)导致该组无法链接。因此,没有完成链接,以前的联系仍然存在。


最后,我们留下了Vince和Tom,并且,Bob和Alice在客户表中被链接为相同的客户。所以客户表可能看起来像......

CustomerName    Customer Link
------------    -------------
Fred            NULL
Bob             YYYYYY
Vince           XXXXXX
Tom             XXXXXX 
Alice           YYYYYY

3 个答案:

答案 0 :(得分:1)

您需要的关系运算符是division,通常称为"the supplier who supplies all parts"

因为您认为Bob有三行与只有一行的Alice相同,所以您应该看{4}。与该链接中的示例不同,您需要从红利表CustomerIdentification派生除数表。在你的情况下,空除数不是问题。

答案 1 :(得分:1)

我有一个问题。如果我们有这些行会发生什么:

INSERT  [dbo].[CustomerIdentification]
VALUES  
        ('Morheus',   'A',  'Passport'),
        ('Morheus',   'B',  'SIN'),

        ('Neo',       'B',  'SIN'),
        ('Neo',       'C',  'Drivers Licence'),

        ('Trinity',   'C',  'Drivers Licence'),
        ('Trinity',   'A',  'Passport') ;

它们是否应该全部放在同一组(被视为同一客户)?

编辑:OP回答所有3人都应被视为同一客户。


在这种情况下会发生什么:

INSERT  [dbo].[CustomerIdentification]
VALUES  
        ('Morheus',   'A',  'Passport'),
        ('Morheus',   'B',  'SIN'),

        ('Neo',       'B',  'SIN'),
        ('Neo',       'C',  'Drivers Licence'),

        ('Trinity',   'C',  'Drivers Licence'),
        ('Trinity',   'D',  'Passport') ;       --- the only change from previous

现在,Morheus应与Neo属于同一客户(同一SIN,不存在其他冲突)。

NeoTrinity应该是同一个客户(同一Drivers Licence,没有其他冲突)。

但是TrinityMorheus拥有不同的护照。

编辑:如果我们首先检查MorheusNeo,则OP会回复所有内容,然后将它们视为一个。然后,Trinity应该被拒绝,因为她的Passport与Morheus发生冲突。

我认为如果我们首先检查NeoTrinity,那么它们应该被视为一个,然后Morheus应该被拒绝,因为他的Passport与{{1}发生冲突}。

我的结论是这个问题在关系术语中是不明确的。仅使用关系逻辑无法解决问题。它可能可以使用变量或分析扩展来解决,例如Trinity

答案 2 :(得分:0)

我第二次看,但没有走远......

首先,将设计从EAV修复为“更多关系”(包括空值,但没关系!):

CREATE TABLE dbo.Customers
(
  CustomerName VARCHAR(20) NOT NULL,
  Passport CHAR(1), 
  SIN CHAR(1), 
  Drivers_Licence CHAR(1), 
  Credit_Card CHAR(1)
) ;

INSERT INTO dbo.Customers (CustomerName, Passport, SIN, Drivers_Licence, Credit_Card) 
   VALUES ('Fred', 'A', 'A', 'A', NULL),
          ('Bob', 'A', NULL, 'B', 'C'), 
          ('Vince', 'A', 'B', NULL, 'C'),
          ('Tom', 'A', 'B', 'A', NULL), 
          ('Alice', NULL, NULL, 'B', NULL);

接下来,自我加入以查找至少具有一个匹配属性的客户对:

SELECT c1.CustomerName, c2.CustomerName
  FROM dbo.Customers c1
       JOIN dbo.Customers c2
          ON c1.CustomerName < c2.CustomerName
             AND (
                  c1.Credit_Card = c2.Credit_Card
                  OR c1.Drivers_Licence = c2.Drivers_Licence
                  OR c1.Passport = c2.Passport
                  OR c1.SIN = c2.SIN
                 );

通过没有相互矛盾的数据的客户对进一步减少这一点,给出零点的“怀疑”:

SELECT c1.CustomerName, c2.CustomerName
  FROM dbo.Customers c1
       JOIN dbo.Customers c2
          ON c1.CustomerName < c2.CustomerName
             AND (
                  c1.Credit_Card = c2.Credit_Card
                  OR c1.Drivers_Licence = c2.Drivers_Licence
                  OR c1.Passport = c2.Passport
                  OR c1.SIN = c2.SIN
                 )
INTERSECT
SELECT c1.CustomerName, c2.CustomerName
  FROM dbo.Customers c1
       JOIN dbo.Customers c2
          ON c1.CustomerName < c2.CustomerName
             AND COALESCE(c1.Credit_Card, c2.Credit_Card, 'x') 
                    = COALESCE(c2.Credit_Card, c1.Credit_Card, 'x') 
             AND COALESCE(c1.Drivers_Licence, c2.Drivers_Licence, 'x')
                    = COALESCE(c2.Drivers_Licence, c1.Drivers_Licence, 'x')
             AND COALESCE(c1.Passport, c2.Passport, 'x')
                    = COALESCE(c2.Passport, c1.Passport, 'x')
             AND COALESCE(c1.SIN, c2.SIN, 'x')
                    = COALESCE(c2.SIN, c1.SIN, 'x');

这导致三对:

CustomerName         CustomerName
-------------------- --------------------
Alice                Bob
Bob                  Vince
Tom                  Vince

接下来,我们需要根据逻辑取消对Bob和Vince,“Bob已经与Alice联系,所以我们必须将Alice带入图片。而Vince已经与Tom联系了所以我们必须将Tom带入图片,“这表明递归或分层(在森林中寻找树木?),此时我鞠躬。