我有两张桌子:
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 :(得分: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
答案 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
;