2016-08-29 47 views
4

我有2个表:SQL:加入或条件

Devices (id (PK)) 
Links (id (PK), device_id_1 (FK), device_id_2 (FK)) 

表示通过链路连接的设备。

我需要选择连接给定设备的所有设备(可以是device_id_1或device_id_2)。我试着用下面的查询做到这一点:

select d2.* 
from Devices as d1 
left outer join Links as l on d1.id in (l.device_id_1, l.device_id_2) 
left outer join Devices as d2 on d2.id in (l.device_id_1, l.device_id_2) 
where d1.id = 398 and d2.id <> 398; 

但只要我说第二JOIN查询返回零行。我究竟做错了什么?

+0

招d2.id <> 398作为AND条件到加入标准。你用目前的方法否定了左连接;实质上使它成为一个内部连接。一个不太优雅的方法是检查d2.id上的null null – xQbert

+0

似乎你可以用链接表来做到这一点。在Devices表中除了id以外是否还有其他数据? – kbball

回答

4

Where子句有效地使最后一个左连接成为内连接。

要解决移动左联接筛选标准来连接标准

select d2.* 
from Devices as d1 
left outer join Links as l on d1.id in (l.device_id_1, l.device_id_2) 
left outer join Devices as d2 on d2.id in (l.device_id_1, l.device_id_2) 
and d2.id <> 398 
where d1.id = 398; 

一个优雅,虽然普遍接受的办法是少得多......

select d2.* 
from Devices as d1 
left outer join Links as l on d1.id in (l.device_id_1, l.device_id_2) 
left outer join Devices as d2 on d2.id in (l.device_id_1, l.device_id_2) 
where d1.id = 398 
    and (d2.id <> 398 OR D2.ID is null) 

我一般认为它是这样的..

当使用外连接时,我通常希望在连接发生之前排除行,这样引擎不必生成如此大的笛卡儿。另外在外连接上,我要返回空记录。但是,如果我在where子句中应用限制,那么除外部连接生成的所有空记录都将被删除,除非我也考虑了NULLS。

在这种情况下,因为您使用的是<> ... <>无法与null进行比较,因此它将排除期望的记录,因为您无法对空值使用相等性检查。

1 = NULL返回NULL和1 <> NULL返回NULL;因此不正确

+0

如果您的398位置是设备位置1,然后位置2是同一设备,则会有重复 – kbball

1

在我看来,你正在尝试做一个存在测试。

它有两种实现方式,具体取决于您的负载。

第二种方案会更好,如果你有大量的数据,第一个1是真正的低基数数据库在性能是不是一个真正的问题:

SELECT d.* (put the list of the column and not *) 
FROM device d 
where exists (select 1 from Links l where l.device_id_1 = d.id and l.device_id_2 = 398 OR l.device_id_1 = 398 and l.device_id_2 = d.id) 



SELECT d.* 
FROM Device d 
WHERE EXISTS (SELECT 1 FROM Links l where l.device_id_1 = d.id and l.device_id_2 = 398) 
UNION ALL 
SELECT d.* 
FROM Device d 
WHERE EXISTS (SELECT 1 FROM Links l where l.device_id_2 = d.id and l.device_id_1 = 398) 

您可能要改造根据您的数据,将所有联盟全部添加到联盟中

1

此作品并不包含重复项。只要运行两个查询和联合在一起:

SELECT D.id 
FROM Devices D 
INNER JOIN Links L 
ON D.id = L.device_id_1 
WHERE D.id <> 398 
AND L.device_id_2 = 398 
UNION 
SELECT D.id 
FROM Devices D 
INNER JOIN Links L 
ON D.id = L.device_id_2 
WHERE D.id <> 398 
AND L.device_id_1 = 398 

这里测试:http://sqlfiddle.com/#!9/e1269/6

0

我们可以从@ kbball的解决方案删除设备表

SELECT L.device_id_1 
FROM Links L 
WHERE L.device_id_2 = 398 
    -- AND L.device_id_1!=398 if self-links are possible 
UNION 
SELECT L.device_id_2 
FROM Links L 
WHERE L.device_id_1 = 398 
    -- AND L.device_id_2!=398 if self-links are possible 
;