2016-08-29 2 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 в качестве И условие к критерию соединения. Вы отрицаете левое соединение своим текущим подходом; по сути дела делая его внутренним соединением. Менее элегантный подход заключается в проверке нулевого значения на d2.id – xQbert

+0

Похоже, вы могли бы сделать это только с помощью таблицы ссылок.Есть ли другие данные, которые вы хотите в таблице Устройства, отличные от 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.

В этом случае, поскольку вы используете <> ... <> не может сравниться с нулевым значением, он будет исключать нужные записи, поскольку вы не можете использовать проверку равенства на нулевом значении.

1 = NULL возвращает NULL и 1 <> NULL возвращает NULL; таким образом, это не так.

+0

Будут дубликаты, если у вас есть 398 позиция 1 с устройством, а затем позиция 2 с тем же устройством – kbball

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, в зависимости от ваших данных

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 
; 
Смежные вопросы