Как вы выполняете внешние соединения "много ко многим таблицам"?
-
21-08-2019 - |
Вопрос
У меня есть 3 таблицы: foo, foo2bar и bar.foo2bar - это карта "многие ко многим" между foo и bar.Вот его содержание.
select * from foo
+------+
| fid |
+------+
| 1 |
| 2 |
| 3 |
| 4 |
+------+
select * from foo2bar
+------+------+
| fid | bid |
+------+------+
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 2 | 3 |
| 4 | 4 |
+------+------+
select * from bar
+------+-------+------+
| bid | value | zid |
+------+-------+------+
| 1 | 2 | 10 |
| 2 | 4 | 20 |
| 3 | 8 | 30 |
| 4 | 42 | 30 |
+------+-------+------+
Что я хочу запросить, так это: "Дайте мне список всех fid и значений с zid равным 30".
Я ожидаю ответа по всем fid, так что результат будет выглядеть следующим образом:
+------+--------+
| fid | value |
+------+--------+
| 1 | null |
| 2 | 8 |
| 3 | null |
| 4 | 42 |
+------+--------+
Решение
SELECT * FROM foo
LEFT OUTER JOIN (foo2bar JOIN bar ON (foo2bar.bid = bar.bid AND zid = 30))
USING (fid);
Протестировано на MySQL 5.0.51.
Это не подзапрос, он просто использует круглые скобки для указания приоритета соединений.
Другие советы
SELECT * FROM
foo LEFT JOIN
(
Foo2bar JOIN bar
ON foo2bar.bid = bar.bid AND zid = 30
)
ON foo.fid = foo2bar.fid;
непроверенный
Если вы не получаете обратно строку для fid = 3, значит, ваш сервер неисправен.
Этот код должен делать то, что, я думаю, вы хотите:
SELECT
F.fid,
SQ.value
FROM
dbo.Foo F
LEFT OUTER JOIN
(
SELECT F2B.fid, B.value
FROM dbo.Bar B
INNER JOIN dbo.Foo2Bar F2B ON F2B.bid = B.bid WHERE B.zid = 30
) SQ ON SQ.fid = F.fid
Имейте в виду, что можно вернуть два значения для fid, если оно связано с двумя столбцами с zid, равным 30.
Проработав это, вы можете начать с вашего выбора.Не включайте столбцы, которые вы не хотите видеть в конечном итоге.
SELECT foo.fid, bar.value
Затем мы можем выполнить предложение WHERE, которое, как вы можете видеть, точно так же, как вы его выразили.
SELECT foo.fid, bar.value
WHERE bar.zid = 30
Теперь сложная часть - соединить вещи вместе для нашего предложения FROM, используя левые соединения, потому что мы хотим видеть каждый fid, независимо от того, есть промежуточные совпадения или нет:
SELECT foo.fid, bar.value
FROM foo
LEFT JOIN foo2bar ON foo.fid = foo2bar.fid
LEFT JOIN bar ON foo2bar.bid = bar.bid
WHERE bar.zid = 30
OR bar.zid IS NULL
FWIW, вопрос на самом деле не о соотношении "многие ко многим":это можно было бы довольно просто сделать как объединение.
Реальное соотношение "многие ко многим" в SQL - это ПЕРЕКРЕСТНОЕ СОЕДИНЕНИЕ