【问题标题】:Full Outer Join self with null values具有空值的完全外连接自身
【发布时间】:2018-05-01 13:46:45
【问题描述】:

我想做一个包含空值的完整外部自连接。例如,如果表 Data 看起来像:

N   Name   Val
--------------
1   ABC    8
1   DEF    7
2   ABC    9
2   XYZ    6

(其中N 是一个通用索引列,用于在顺序组上启用自联接),我这样做:

SELECT COALESCE(a.n, b.n) as n, COALESCE(a.Name, b.Name) as Name, a.Val as A, b.Val as B
FROM Data a 
     FULL OUTER JOIN Data b on a.N = b.N - 1 and a.Name = b.Name

我想要:

N  Name  A    B
---------------
1  ABC   8    9
1  DEF   7    NULL
1  XYZ   NULL 6

但我得到的更像是交叉连接:

n  Name  A    B
--------------
1  ABC   8    9
1  DEF   7    NULL
2  ABC   9    NULL
2  XYZ   6    NULL
1  ABC   NULL    8
1  DEF   NULL    7
2  XYZ   NULL    6

如何执行此完全外部联接以获得精简的自联接结果?

注意:实际上列N是一个通用索引,因此需要命名N的值的解决方案并不实用。)

【问题讨论】:

    标签: sql-server tsql outer-join self-join


    【解决方案1】:

    到目前为止,我只能将其视为工会。以及左右连接,因为您所追求的标准发生了变化。

    SELECT COALESCE(a.Name, b.Name) as Name, a.Val as A, b.Val as B
    FROM Data a 
    LEFT JOIN Data b on a.Name = b.Name   
       and B.N = 2
    WHERE A.N = 1 
    UNION 
    SELECT COALESCE(a.Name, b.Name) as Name, a.Val as A, b.Val as B
    FROM Data a 
    RIGHT JOIN Data b on a.Name = b.Name   
       and A.N = 1
    WHERE B.N = 2
    

    给我们:

    +------+---+----+
    | NAME | A |  B |
    +------+---+----+
    | ABC  | 8 |  9 |
    | DEF  | 7 |    |
    | XYZ  |   |  6 |
    +------+---+----+
    

    然而,这依赖于一个硬编码的 N 值,我认为这不是很有用...工作得更好。

    【讨论】:

    • 是的,这就是诀窍:N 是一个允许顺序自连接的索引列,所以我不能使用N 的命名值....
    • 刚刚更新了问题以明确N 是输出的一部分。
    【解决方案2】:

    由于我们要处理广义自连接索引列N,让我们进一步扩展样本集:

    create table #Data (n int, name char(3), val int)
    insert into #Data values (1, 'ABC',8)
    insert into #Data values (1, 'DEF',7)
    insert into #Data values (2, 'ABC',9)
    insert into #Data values (2, 'XYZ',6)
    insert into #Data values (3, 'ABC',9)
    insert into #Data values (3, 'DEF',5)
    insert into #Data values (3, 'XYZ',4)
    

    对于这个示例,我们希望 SQL 产生这个输出:

    N  Name  A    B
    ---------------
    1  ABC   8    9
    1  DEF   7    NULL
    1  XYZ   NULL 6
    2  ABC   9    9
    2  DEF   NULL 5
    2  XYZ   6    4
    

    以下代码适用于一般情况:

    SELECT COALESCE(a.n, b.n-1) as i, COALESCE(a.Name, b.Name) as Name, a.Val as A, b.Val as B
    FROM #Data a 
        FULL OUTER JOIN #Data b ON a.N = b.N - 1 AND a.Name = b.Name
    WHERE a.n < (SELECT MAX(n) FROM #Data) -- Deals with end index case
        OR (a.n is null AND b.n-1 IN (SELECT DISTINCT n FROM #Data))
    ORDER BY COALESCE(a.n, b.n-1), Name
    

    要了解为什么会这样,一个很好的中间步骤是注意当a.N = 1 我们想要n = 1 来自的行:

    SELECT COALESCE(a.n, b.n - 1) as n, COALESCE(a.Name, b.Name) as Name,
        a.Val as A, b.Val as B 
    FROM #Data a
        FULL OUTER JOIN #Data b ON a.N = b.N - 1 AND a.Name = b.Name
    

    【讨论】:

      【解决方案3】:

      请看下面的代码:

      create table Data (n int, name char(3), val int)
      
      insert into data values (1, 'ABC',8)
      insert into data values (1,   'DEF',    7)
      insert into data values (2 ,  'ABC' ,   9)
      insert into data values (2  , 'XYZ',    6)
      
      SELECT COALESCE(a.Name, b.Name) as Name, a.Val as A, b.Val as B
      FROM Data a 
           FULL OUTER JOIN Data b on a.N = b.N - 1 and a.Name = b.Name
      

      输出是这样的:

      两边都有空值。

      【讨论】:

      • 哎呀...是的,你是对的;我以一种没有显示这一点的方式限制我的测试数据。我正在修复原始问题以反映这一点。 (请注意,它仍然没有按照我想要的方式加入。)
      【解决方案4】:

      也许是这样的:

      SELECT [Name]
             ,[1]
             ,[2]
      FROM [table]
      PIVOT
      (
          MAX([val]) FOR [N] IN ([1], [2])
      ) PVT;
      

      【讨论】:

      • 聪明。但是N 列是一个大索引,所以我需要一个可概括的情况,我不必命名N 的每个可能值。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-22
      • 2015-12-11
      • 2023-03-20
      • 2012-01-06
      • 2013-03-11
      相关资源
      最近更新 更多