SQL:在条件中与OR连接

时间:2016-08-29 12:58:24

标签: sql postgresql join

我有两张桌子:

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,查询就会返回零行。我做错了什么?

4 个答案:

答案 0 :(得分: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,否则将删除从外部联接生成的所有空记录。

在这种情况下,因为您正在使用&lt;&gt; ...&lt;&gt;无法与null进行比较,因此它会排除所需的记录,因为您无法对空值进行等式检查。

1 = NULL返回NULL并且1&lt;&gt; NULL返回NULL;因此不是真的

答案 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)

您可能希望将UNION ALL转换为UNION,具体取决于您的数据

答案 2 :(得分: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

答案 3 :(得分:0)

我们可以从@ kbball的解决方案中删除Devices表

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
;