比较两个表中的主要/别名组

Posted

技术标签:

【中文标题】比较两个表中的主要/别名组【英文标题】:compare primary/alias groups across two tables 【发布时间】:2018-11-28 19:57:22 【问题描述】:

生日,

我们有两个包含完全相同结构的表。有两列“PrimaryAddress”和“AliasAddress”。这些用于电子邮件地址和别名。我们希望找到任何需要添加到任一方 的记录以保持记录同步。问题是一个表中的主名称可能在另一个表中列为别名。好消息是地址不会在“AliasAddress”列中出现两次。

TABLE A
PrimaryAddress~~~~~AliasAdress
chris@work~~~~~~~~~chris@home
chris@work~~~~~~~~~c@work
chris@work~~~~~~~~~theboss@work
chris@work~~~~~~~~~thatguy@aol
bob@test~~~~~~~~~~~test1@test
bob@test~~~~~~~~~~~charles@work
bob@test~~~~~~~~~~~chuck@aol
sally@mars~~~~~~~~~sally@nasa
sally@mars~~~~~~~~~sally@gmail

TABLE B
PrimaryAddress~~~~~AliasAdress
chris@home~~~~~~~~~chris@work
chris@home~~~~~~~~~c@work
chris@home~~~~~~~~~theboss@work
chris@home~~~~~~~~~thatguy@aol
bob@test~~~~~~~~~~~test1@test
bob@test~~~~~~~~~~~charles@work
sally@nasa~~~~~~~~~sally@mars
sally@nasa~~~~~~~~~sally@gmail
sally@nasa~~~~~~~~~ripley@nostromo

预期结果是从两个表中返回以下缺失记录:

bob@test~~~~~~~~~~~chuck@aol
sally@nasa~~~~~~~~~ripley@nostromo

请注意,chris@* 块是完全匹配的,因为无论哪个地址被视为主要地址,所有别名(加上主要)的总和仍然相同。哪个地址是主地址并不重要,因为整个主组的总和包含两个表中的所有条目。

我不介意这是否在 A->B 和 B->A 两个通道中运行,但我无法理解解决方案。

任何帮助表示赞赏:)

【问题讨论】:

对不起,但是随着栏目内容的翻转,我真的无法弄清楚这里的真正要求。您能否就您正在寻找的内容添加更多细节,或者改写一下? (“Primary”和“Sum”的使用让我特别困惑。) “总和”是指对每个主地址的所有行进行分组,选择所有地址,然后比较不同的结果。在我上面的示例中,两个表中的“chris”记录被认为是相同的,因为它们有 4 行,当它们组合在一个列表中并删除重复项时,会产生相同的 5 个电子邮件地址。 如果存在“重叠”值,需要做什么?例如,如果我们在表 A 中添加 ("bob@test", "c@work"),“bob”和“chris”块现在是否会被视为一个块? 这不可能发生:“好消息是地址不会在“AliasAddress”列中出现两次。” 【参考方案1】:
drop TABLE #TABLEA
CREATE TABLE #TABLEA
    ([PrimaryAddress] varchar(10), [AliasAdress] varchar(12))
;

INSERT INTO #TABLEA
    ([PrimaryAddress], [AliasAdress])
VALUES
    ('chris@work', 'chris@home'),
    ('chris@work', 'c@work'),
    ('chris@work', 'theboss@work'),
    ('chris@work', 'thatguy@aol'),
    ('bob@test', 'test1@test'),
    ('bob@test', 'charles@work'),
    ('bob@test', 'chuck@aol'),
    ('sally@mars', 'sally@nasa'),
    ('sally@mars', 'sally@gmail')
;


drop TABLE #TABLEB
CREATE TABLE #TABLEB
    ([PrimaryAddress] varchar(10), [AliasAdress] varchar(15))
;

INSERT INTO #TABLEB
    ([PrimaryAddress], [AliasAdress])
VALUES
    ('chris@home', 'chris@work'),
    ('chris@home', 'c@work'),
    ('chris@home', 'theboss@work'),
    ('chris@home', 'thatguy@aol'),
    ('bob@test', 'test1@test'),
    ('bob@test', 'charles@work'),
    ('sally@nasa', 'sally@mars'),
    ('sally@nasa', 'sally@gmail'),
    ('sally@nasa', 'ripley@nostromo')
;

试试下面的

select a.PrimaryAddress,a.AliasAdress  from #TABLEA a left join #TABLEB b on a.AliasAdress=b.AliasAdress or b.PrimaryAddress=a.AliasAdress 
where b.PrimaryAddress is null
union all
select a.PrimaryAddress,a.AliasAdress  from #TABLEB a left join #TABLEA b on a.AliasAdress=b.AliasAdress or b.PrimaryAddress=a.AliasAdress 
where b.PrimaryAddress is null

【讨论】:

【参考方案2】:

因此,您想比较表 A 和 B,并在任一表中查找不匹配的行。 outer join 怎么样,然后查找 NULL 值:

SELECT ta.*, tb.*

FROM       table_a ta

FULL OUTER JOIN table_b tb ON  tb.PrimaryAddress = ta.PrimaryAddress 
                           AND tb.AliasAddress = ta.AliasAddress

WHERE ta.PrimaryAddress IS NULL
   OR tb.PrimaryAddress IS NULL

如果我正确理解了这个问题,这应该会返回您所要求的内容。

【讨论】:

不幸的是。请注意,以这种方式匹配的行非常少。这是该查询的结果:chris@work chris@home NULL NULL chris@work c@work NULL NULL chris@work theboss@work NULL NULL chris@work thatguy@aol NULL NULL bob@test chuck@aol NULL NULL sally@mars sally@nasa NULL NULL sally@mars sally@gmail NULL NULL NULL NULL chris@home chris@work NULL NULL chris@home c@work NULL NULL chris@home theboss@work NULL NULL chris@home thatguy@aol NULL NULL sally@nasa sally@mars NULL NULL sally@nasa sally@gmail NULL NULL sally@nasa ripley@nostromo 那我不太确定你想要什么。上面的查询只返回表 a 或表 b 唯一的所有条目。在没有任何其他信息的情况下,这是(据我所知)识别新条目的唯一方法。【参考方案3】:

我是这样做的,最后有点举手之劳。

第一步,确定要比较的项目集。这是:

对于“主”值,在 Alias 中找到的所有值 也包括“主要”值(以涵盖 nasa/nostromo 案例)

表(A 或 B)中的集合由其主值标识。真正让它变得困难的是,两个表(sally@mars、sally@nasa)之间没有共享主要值。所以我们可以比较集合,但我们必须能够分别“返回”到每张桌子上的主桌子(例如,从桌子 B 中脱颖而出的可能是 sally@nasa / ripley@nostroomo,但我们必须添加 sally@ mars / ripley@nostromo 到表 A)

如果在表中,主值显示为不同主值的别名(例如,在表 A 中,chris@work 显示为 bob@test 的别名),则会出现主要问题。为了理智起见,我假设这不会发生……但如果发生了,问题就会变得更加困难。

此查询用于在 B 中添加不在 A 中的缺失项,其中 A 和 B 的 PrimaryAddress 相同:

;WITH setA (SetId, FullSet)
 as (--  Complete sets in A
     select PrimaryAddress, AliasAdress   
      from A
     union select PrimaryAddress, PrimaryAddress
      from A
    )
,setB (SetId, FullSet)
 as (--  Complete sets in B
     select PrimaryAddress, AliasAdress   
      from B
     union select PrimaryAddress, PrimaryAddress
      from B
    )
,NotInB (Missing)
 as (--  What's in A that's not in B
     select FullSet
      from setA
     except select FullSet  --  This is the secret sauce. Definitely worth your time to read up on how EXCEPT works.
      from setB
    )
--  Take the missing values plus their primaries from A and load them into B
INSERT B (PrimaryAddress, AliasAdress)
 select A.PrimaryAddress, nB.Missing
  from NotInB nB
   inner join A
    on A.AliasAdress = nb.Missing

将表格反转(从“NotInB”开始)再次运行它,以对 A 执行相同操作。

但是

对“in B not in A”的样本数据执行此操作会将 (sally@nasa, ripley@nostromo) 添加到 A,并且由于这是不同的主要数据,它会创建一个新集合,因此不会解决这个问题。它很快变得丑陋。从这里说出来:

通过两次,一次为 A 不在 B 中,一次为 B 不在 A 中 每次通过,必须做两次检查 首先检查上面的内容:主地址匹配的A中不是B中的内容,并添加它 第二次检查很难看:A 中的内容不在 B 中,其中来自 A 的主地址不是 B 中的主地址,因此必须是别名。在这里,在 B 的别名列表中找到 A 的主地址,在 B 中获取用于该集合的主键,并使用这些值在 B 中创建行。

【讨论】:

【参考方案4】:

好的,我们就是这样做的……因为它变得很痛苦,我们运行了一个程序,将每个条目的主地址添加为别名:xx@xx -> xx@xx,以便 所有的地址都被列为每个用户的别名。这类似于@Phillip Kelly 上面所做的。然后我们运行了以下代码:(虽然很乱,但它可以工作;也可以一次性完成)

SELECT 'Missing from B:' as Reason, TableA.[primary] as APrimary, TableA.[alias] as AAlias, TableB.[primary] as BPrimary,TableB.[alias] as BAlias into #A FROM dbo.TableA LEFT OUTER JOIN TableB ON TableB.alias = TableA.alias 
SELECT 'Missing from A:' as Reason,TableA.[primary] as APrimary, TableA.[alias] as AAlias, TableB.[primary] as BPrimary,TableB.[alias] as BAlias into #B FROM dbo.TableB LEFT OUTER JOIN TableA ON TableA.alias = TableB.alias 

select * from #A
select * from #B

UPDATE #A 
   SET #A.APrimary = #B.BPrimary 
   FROM #B  INNER JOIN  #A ON #A.APrimary = #B.BPrimary
   WHERE #A.BPrimary IS NULL

UPDATE #B 
   SET #B.BPrimary = #A.APrimary 
   FROM #B  INNER JOIN  #A ON #B.BPrimary = #A.BPrimary
   WHERE #B.APrimary IS NULL

select * from #A
select * from #B

select * into #result from (
select Reason, BPrimary as [primary], BAlias as [alias] from #B where APrimary IS NULL
union
select Reason, APrimary as [primary], AAlias as [alias] from #A where BPrimary IS NULL
) as tmp

select * from #result

drop table #A
drop table #B
drop table #result

GO

【讨论】:

以上是关于比较两个表中的主要/别名组的主要内容,如果未能解决你的问题,请参考以下文章

如何比较 Oracle 中的记录集或记录组?

mysql查询加入,比较两个表并返回第一个表中的所有记录

如何通过比较两个表中的两列来更新表中的列

有效地比较两个表中的列

Mysql比较两个表中的两列

优化比较两个 MySQL 大表中的数据