Могу ли я указать порядок, в котором происходят объединения? - PullRequest
3 голосов
/ 04 октября 2011

Скажем, у меня есть три таблицы, A, B и C. Концептуально A (опционально) имеет одну B, а B (всегда) имеет одну C.

Table A:
a_id
... other stuff

Table B:
a_fk_id (foreign key to A.a_id, unique, primary, not null)
c_fk_id (foreign key to C.c_id, not null)
... other stuff

Table C:
c_id
... other stuff

Я хочу выбрать Все записи из Aа также связанные с ними записи из B и C, если таковые имеются.Однако данные B и C должны появляться в результате только в том случае, если присутствуют B и C.

Я чувствую, что хочу сделать:

SELECT *
FROM
    A
    LEFT JOIN B on A.a_id=B.a_fk_id
    INNER JOIN C on B.c_fk_id=C.c_id

Но объединения, похоже, осталисьассоциативно (первое соединение происходит перед вторым соединением), поэтому это не даст записи из A, у которых нет записи в C.

AFAICT Я должен использовать подзапросы, что-то вроде:

SELECT *
FROM
    A
    LEFT JOIN (
        SELECT * FROM B INNER JOIN C ON B.c_fk_id=C.c_id
    ) as tmp ON A.id = tmp.a_fk_id

но как только у меня есть пара таких отношений в запросе (в действительности у меня может быть два или три вложенных), я беспокоюсь как о сложности кода, так и об оптимизаторе запросов.

Есть ли способ указать порядок соединения, кроме этого метода подзапроса?

Спасибо.

Ответы [ 4 ]

4 голосов
/ 04 октября 2011

В SQL Server вы можете сделать

SELECT *
FROM   a
       LEFT JOIN b
                 INNER JOIN c
                   ON b.c_fk_id = c.c_id
         ON a.id = b.a_fk_id  

Позиция предложения ON означает, что LEFT JOIN в b логически происходит последним. Насколько я знаю, это стандарт ( заявлено, что здесь предписано ANSI ), но я уверен, что отрицательные голоса сообщат мне, если он не работает в MySQL!

2 голосов
/ 04 октября 2011

Редактировать: И это то, что я получаю за разговор быстрее, чем я думаю. Мое предыдущее решение не работает, потому что 'c' еще не присоединился. Давайте попробуем это снова.

Мы можем использовать предложение WHERE, чтобы ограничить результаты только теми, которые соответствуют критериям, которые вы ищете, где C имеет допустимое значение (IS NOT NULL) или B не имеет значения (IS NULL). Как это:

SELECT *
FROM a
    LEFT JOIN b ON (b.a = a.a)
    LEFT JOIN c ON (c.b = b.b)
WHERE (c.c IS NOT NULL OR b.b IS NULL);

Без ГДЕ Результаты:

mysql> SELECT * FROM a LEFT JOIN b ON (b.a = a.a) LEFT JOIN c ON (c.b = b.b);
+------+------+------+------+------+
| a    | a    | b    | c    | b    |
+------+------+------+------+------+
|    1 |    1 |    1 |    1 |    1 |
|    1 |    1 |    2 | NULL | NULL |
|    2 |    2 |    3 |    2 |    3 |
|    3 | NULL | NULL | NULL | NULL |
|    4 | NULL | NULL | NULL | NULL |
+------+------+------+------+------+

С ГДЕ результатами:

mysql> SELECT * FROM a LEFT JOIN b ON (b.a = a.a) LEFT JOIN c ON (c.b = b.b) WHERE (c.c IS NOT NULL OR b.b IS NULL);
+------+------+------+------+------+
| a    | a    | b    | c    | b    |
+------+------+------+------+------+
|    1 |    1 |    1 |    1 |    1 |
|    2 |    2 |    3 |    2 |    3 |
|    3 | NULL | NULL | NULL | NULL |
|    4 | NULL | NULL | NULL | NULL |
+------+------+------+------+------+
0 голосов
/ 05 октября 2011

Ну, я тоже придумал другое решение и выкладываю его для полноты (хотя на самом деле я использую ответ Мартина).

Используйте ПРАВИЛЬНОЕ СОЕДИНЕНИЕ:

SELECT 
    *
FROM
    b
    INNER JOIN c ON b.c_fk_id = c.c_id
    RIGHT JOIN a ON a.id = b.a_fk_id

Я почти уверен, что каждая часть, которую я читал о JOINS, говорила о том, что RIGHT JOINs были бессмысленными, но вот ты где.

0 голосов
/ 04 октября 2011

Да, для этого вы используете STRAIGHT_JOIN.
При использовании этого ключевого слова соединение будет происходить в указанном вами порядке.

См .: http://dev.mysql.com/doc/refman/5.5/en/join.html

...