【问题标题】:A complex sql issue一个复杂的sql问题
【发布时间】:2026-01-08 07:25:01
【问题描述】:

我有这两张桌子:

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')

基本上,一个客户(来自客户表)可以有多个标识。例如,Fred 有护照、犯罪和驾驶执照,所有 3 的值都是 A(值也可能不同)。

这是我想要的输出:

测试 1:价值为 A 的护照

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

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

测试 2:值为 B 的 SIN

下一个常见的标识是具有值 B 的 SIN,存在于 Vince 和 Tom 中。 Vince 和 Tom 确实是同一个客户,因为他们的 Passport 也具有相同的值 (A)。两者的第三个身份证明具有不同的 IDType、信用卡和驾驶执照。因此它们可以被链接。因此,我们将 vince 和 Tom 链接为客户表中的同一客户。

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

这适用于 Fred 和 Tom。两者都有价值A的护照。两者都有共同的 SIN,但是,两者的 SIN 值不同。它的 A 代表 Fred,B 代表 Tom。因此,他们不是同一个客户。未完成链接。

测试 4:值为 B 的驾驶执照

这适用于 Bob 和 Alice。他们确实是同一个客户,因为 Bob 和 Alice 都拥有价值 B 的驾驶执照。 Bob 有另外 2 个 ID,而 Alice 没有,这很好,因为这些 ID 对于 Bob 来说是唯一的。因此,我们将 Bob 和 Alice 链接为客户表中的同一客户。

测试 5:价值 C 的信用卡

这适用于 Bob 和 Vince。

但是 Bob 已经与 Alice 关联,所以我们必须将 Alice 带入图片中。 文斯已经和汤姆有联系,所以我们必须把汤姆带入画面。

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


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

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

【问题讨论】:

  • 作业伪装成问题?
  • @a'b'c'd'e'f'g'h',这里没有太多伪装 =)
  • 有趣的问题,但这是作业吗?如果是这样,用作业标签标记它是公平的。您是否也尝试过自己解决这个问题?您是否在某个特定领域苦苦挣扎?
  • 没有作业。我以前用不同的语言问过这个问题,但人们听不懂。 (*.com/questions/8014815/…) 所以我不得不把它简化了很多,这使它看起来像家庭作业。我真的试过了,但我无法理解逻辑。另一个线程上的汤姆拍得很好,但他的逻辑仍然没有产生预期的结果。

标签: sql


【解决方案1】:

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

因为您认为拥有 3 行的 Bob 与只有 1 行的 Alice 相同,所以您应该查看division with remainder。与该链接中的示例不同,您需要从除数表CustomerIdentification 导出除数表。在您的情况下,空除数不是问题。

【讨论】:

  • 如果您能回复示例代码,我将不胜感激。我对此有点筋疲力尽。
  • 我道歉 onedaywhen。我会读那些文章。只是我已经做了一个星期了,我有点脑筋急转弯。如果有人能提出一个可行的解决方案......我想我是不合理的。
【解决方案2】:

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

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,没有其他冲突)。

并且Neo 应该是与Trinity 相同的客户(相同的Drivers Licence,没有其他冲突)。

但是TrinityMorheus 的护照不同。

编辑:OP回复说,如果我们首先检查MorheusNeo,那么它们应该被视为一个。然后,Trinity 应该被拒绝,因为她的 Passport 与Morheus 冲突。

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

我的结论是问题在关系方面定义不明确。仅使用关系逻辑是无法解决的。它可能可以使用变量或分析扩展来解决,例如ROW_NUMBER().

【讨论】:

  • 好的,感谢您尝试帮助解决我的问题。在您的第一个测试用例中,Morphese 和 Trinity 具有相同的护照,值为 A,其他两个 IDType SIN 和 DRIV LIC 不同,所以是的,它们是同一个客户。现在,对于 morphese 和 neo 也是一样的,因为它们都有 SIN B 并且 Passport 和 Driv License 是不同的 IDType。但 Morphese 已经与 Trin 联系在一起,所以我们必须将她带入等式。由于在这种情况下,Trinity 与 Neo 没有冲突,因为两者的 Driv Lic C 和 SIN 和 Passport 是不同的,实际上它们都是同一个客户,应该链接。
  • 在第二种情况下,morphese 与 neo 相同,因为两者都有 SIN B,而其他两个不同。所以我们把它们联系起来。现在我们检查 neo 和 trinity,他们都有 Driv Lic C,而另外两个不同,但是 Neo 已经与 morphese 相关联,所以我们必须带上 morphese 并查看 morphese 是否适合等式。但是 morphese 没有,因为他与三位一体的 Passport A Vs Passport D 发生冲突。所以没有完成链接。最终结果,只有 morphese 和 neo 是同一个客户。
  • 如果您想知道数据的不同顺序会产生不同的结果,那么是的,这完全有可能。在我的场景中可以接受。
  • 哇,我希望这个问题没有我想象的那么难,并且有人能够很快地帮助解决这个问题。估计不容易……
  • @Vince:这不是很困难。问题是您可以根据检查组合的顺序获得不同的结果。 SQL 和关系运算符在结果相同时起作用,无论使用什么顺序。因此,您的问题在关系方面定义不明确。
【解决方案3】:

我又看了看,但没走多远……

首先,将 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 带入图片”,这表明递归或分层(在森林中寻找树木?),此时我退出了。

【讨论】:

  • 嗨 onedaywhen,我想如果你看看我在我的一个回复中发布的另一个帖子,Tom 实际上也做了同样的事情,甚至没有改变桌子的设计。此外,我必须补充一点,改变桌子设计不是一种选择。第三方供应商为我提供了数据库设计,并且无法更改。感谢您的尝试,我知道这是一个非常特殊的问题。我认为自己在 SQL 方面相当出色,但即使我现在也被难住了一个星期。
  • @Vince:“Tom 实际上做了同样的事情,甚至没有改变表格设计”——您可以在我的“更相关”的查询中转换第三方的“EVA”结构(例如 VIEW)例如使用枢轴。但是您是否知道汤姆的答案以“我已经稍微更改了您的数据模型...”开头!
  • 他更改了基本数据类型和列名,但从根本上说,他的表格代表了我所做的。顺便说一句,我确实设法解决了这个问题。