具有空值的完全外连接自身

Posted

技术标签:

【中文标题】具有空值的完全外连接自身【英文标题】: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的值的解决方案并不实用。)

【问题讨论】:

【参考方案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 的每个可能值。

以上是关于具有空值的完全外连接自身的主要内容,如果未能解决你的问题,请参考以下文章

oracle左外连接不显示右空值

左外连接和空值转换

左外连接和右外连接的区别

SQL内连接与外连接的区别

memsql 是不是支持完全外连接?

SQL 外连接时间替换空值