显示来自两个不同表的两列之间的差异(比较表)

Posted

技术标签:

【中文标题】显示来自两个不同表的两列之间的差异(比较表)【英文标题】:Show the difference between two columns from two different tables (comparison table) 【发布时间】:2015-04-06 20:02:00 【问题描述】:

我有两个具有相同结构和几乎相同内容的表。主要区别在于一个值列可能与第二个表中的“等效”列不同。最重要的是,一条记录可能会出现在一个表中,但不会出现在另一个表中。

我的桌子:

Table1(idA、valueA、valueB、valueC(可空)、valueX) Table2(idB、valueA、valueB、valueC(可空)、valueY)

想要的结果:

表3(值A、值B、值C、值X、值Y)

前提条件:

'id' 是主键,在两个表中可以不同 'valueC' 是唯一可以具有 null 值的列。但是,两个表中的值将相同。 如果记录出现在表 1 中但未出现在表 2 中,'valueY' 应为 null 如果记录出现在表 2 中但不在表 1 中,'valueX' 应为 null

例子:

表1 1 | 1001 | 2001 | 3001 | 100.0 2 | 1002 | 2002 | 3002 | 95.0 3 | 1003 | 2003 | (空) | 113.0 4 | 1004 | 2004 | 3004 | 75.0

表2 23 | 1001 | 2001 | 3001 | 100.0 24 | 1002 | 2002 | 3002 | 94.0 25 | 1003 | 2003 | (空) | 116.0 26 | 1005 | 2005 | 3005 | 32.0

期望的结果应该是:

表3 1001 | 2001 | 3001 | 100.0 | 100.0 1002 | 2002 | 3002 | 95.0 | 94.0 1003 | 2003 | (空) | 113.0 | 116.0 1004 | 2004 | 3004 | 75.0 | (空) 1005 | 2005 | 3005 | (空) | 32.0

我想我已经想出了一个解决方案,但它非常慢,我不确定这是否真的是最简单的方法。

SELECT valueA, valueB, valueC, valueX, valueY
FROM (
    (SELECT t1.valueA, t1.valueB, t1.valueC, t1.valueX, t2.valueY
    FROM Table1 t1
    LEFT JOIN Table2 t2
        ON t1.valueA = t2.valueA 
        AND t1.valueB = t2.valueB 
    WHERE t1.valueC = t2.valueC OR t1.valueC IS NULL OR t2.valueC IS NULL)
    UNION
    (SELECT t2.valueA, t2.valueB, t2.valueC, t1.valueX, t2.valueY
    FROM Table2 t2
    LEFT JOIN Table1 t1
        ON t1.valueA = t2.valueA 
        AND t1.valueB = t2.valueB 
    WHERE t1.valueC = t2.valueC OR t1.valueC IS NULL OR t2.valueC IS NULL)
    );

我希望有人能想出一个更“优雅”的解决方案。作为旁注,我使用的是 Oracle DB。提前感谢您的时间和帮助!

【问题讨论】:

如果两个表之间的 pk 可以不同,你如何匹配记录,即,你如何确定“一个记录可能出现在一个表中,但不能出现在另一个表中” 就我的目的而言,匹配基于值 A、B 和 C 的记录就足够了(此处的示例已简化,我的实际表有 7 列匹配)。因此,如果 A、B 和 C 匹配,我们可以假设它是相同的记录。如果其中一个没有,那就是不同的记录。因此,如果您在 table1 中有 ABC 值但在 table2 中没有匹配项,则 table2 中缺少它。 然后在 A、B 和 C 列上添加一个键。听起来这可能是一个“自然键”。如果你不能,由于重复而这样做,那么你不能“假设它们是相同的记录” 我想您可以将其视为“自然键”,但 C 可以为 null 除外。另一个问题是我不允许更改表,因为这是我的客户定义并在项目中使用的方式。 可以添加索引吗?如果是这样,请添加唯一索引。您可以在包含可为空属性的复合键上执行此操作,这将首先防止重复。 【参考方案1】:

此查询提供所需的输出。我不知道它是否更优雅,但肯定更短;-)

select valueA, valueB, nvl(table1.valueC, table2.valueC) valueC, valueX, valueY 
  from table1
    full join table2 using (valueA, valueB)
  order by valueA

顺便说一句,您的查询只给出了 4 行,没有1004 | 2004 | 3004 | 75.0 | (null),可能您的where 应该是:

where t1.valueC = t2.valueC OR t1.valueC IS NULL or t2.valueC IS NULL

而不是

WHERE t1.valueC = t2.valueC OR (t1.valueC IS NULL AND t1.valueC IS NULL)

重新设计的查询

select a, b, nvl(t1c, t2c) c, x, y
  from (
    select nvl(t1.valueA, t2.valueA) a, nvl(t1.valueB, t2.valueB) b, 
        t1.valueC t1c, t2.valueC t2c, valueX x, valueY y
      from table1 t1 
        full join table2 t2 on (t1.valueA = t2.valueA and t1.valueB = t2.valueB
          and ( t1.valueC = t2.valueC or (t1.valueC is null and t2.valueC is null) ) )
        )
  order by a, b, c

【讨论】:

我想这可能是答案。它绝对比我的查询短得多,速度快 5 倍。你对我的 where 子句也是正确的,我认为我在那里犯了一个错误(我已经用你的更正修改了这个问题)。奇怪的是,当您的查询返回 6438772 条记录时,我的返回 6879424。我希望这是由于我的查询中的另一个问题,我目前似乎看不到。 由于对valueC比较的误解而出现差异。你在主帖中的最后一条评论改变了一些事情。所以我修改了查询,我认为它可以根据所有条件工作。如果将行 (5, 1006, 2006, 3006, 101) 添加到 table1 并将 (27, 1006, 2006, null, 201) 添加到 table2,它会在 valueX 和 valueY 列中产生与您不同的结果,但我认为您不能从不匹配的行中获取这些值。 此外,如果您现在将 table2 中添加的行中的 valueC 从 null 更改为 3007,那么我的查询会给出 7 行结果,您的 - 5 行。我这样做是因为如果 ABC 上的行不匹配,那么我们会列出它们都没有加入。【参考方案2】:

创建表c为(从表a中选择*减去从表B中选择*)联合(从表b中选择*减去从表A中选择*)

【讨论】:

谢谢,但不幸的是,这不起作用,因为它还连接了 ID 和 valueX 和 valueY 列,每个表中可能有不同的值。此外,它不会并列最后两列,这正是我所需要的。 如果您使用我的查询但选择较少的列?

以上是关于显示来自两个不同表的两列之间的差异(比较表)的主要内容,如果未能解决你的问题,请参考以下文章

想比较laravel中不同表的两列

连接来自两个不同表的两列

比较两个excel的两列并返回第三列

如何比较不同数据库中不同表的两列?

如何比较两个表数据的差异

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