Подтвердить что ты не робот

Проблемы с работой LEFT OUTER JOIN

Я думал, что понял, как работают внешние внешние соединения, но у меня есть ситуация, которая не работает, и я не уверен на 100%, если способ структурирования моего запроса неверен или если это проблема с данными.

Для фона у меня есть следующие структуры таблиц MySQL:

mysql> describe achievement;
+-------------+----------------------+------+-----+---------+-------+
| Field       | Type                 | Null | Key | Default | Extra |
+-------------+----------------------+------+-----+---------+-------+
| id          | varchar(64)          | NO   | PRI | NULL    |       |
| game_id     | varchar(10)          | NO   | PRI | NULL    |       |
| name        | varchar(64)          | NO   |     | NULL    |       |
| description | varchar(255)         | NO   |     | NULL    |       |
| image_url   | varchar(255)         | NO   |     | NULL    |       |
| gamerscore  | smallint(5) unsigned | NO   |     | 0       |       |
| hidden      | tinyint(1)           | NO   |     | 0       |       |
| base_hidden | tinyint(1)           | NO   |     | 0       |       |
+-------------+----------------------+------+-----+---------+-------+
8 rows in set (0.00 sec)

и

mysql> describe gamer_achievement;
+----------------+---------------------+------+-----+---------+-------+
| Field          | Type                | Null | Key | Default | Extra |
+----------------+---------------------+------+-----+---------+-------+
| game_id        | varchar(10)         | NO   | PRI | NULL    |       |
| achievement_id | varchar(64)         | NO   | PRI | NULL    |       |
| gamer_id       | varchar(36)         | NO   | PRI | NULL    |       |
| earned_epoch   | bigint(20) unsigned | NO   |     | 0       |       |
| offline        | tinyint(1)          | NO   |     | 0       |       |
+----------------+---------------------+------+-----+---------+-------+
5 rows in set (0.00 sec)

Что касается данных, это то, что я здесь заселял (для краткости были включены только соответствующие столбцы):

+----+------------+------------------------------+
| id | game_id    | name                         |
+----+------------+------------------------------+
| 1  | 1480656849 | Cluster Buster               |
| 2  | 1480656849 | Star Gazer                   |
| 3  | 1480656849 | Flower Child                 |
| 4  | 1480656849 | Oyster-meister               |
| 5  | 1480656849 | Big Cheese of the South Seas |
| 6  | 1480656849 | Hexic Addict                 |
| 7  | 1480656849 | Collapse Master              |
| 8  | 1480656849 | Survivalist                  |
| 9  | 1480656849 | Tick-Tock Doc                |
| 10 | 1480656849 | Marathon Mogul               |
| 11 | 1480656849 | Millionaire Extraordinaire   |
| 12 | 1480656849 | Grand Pearl Pooh-Bah         |
+----+------------+------------------------------+
12 rows in set (0.00 sec)

и

+----------------+------------+--------------+---------+
| achievement_id | game_id    | earned_epoch | offline |
+----------------+------------+--------------+---------+
| 1              | 1480656849 |            0 |       1 |
| 2              | 1480656849 |            0 |       1 |
| 3              | 1480656849 |            0 |       1 |
| 4              | 1480656849 |   1149789371 |       0 |
| 7              | 1480656849 |   1149800406 |       0 |
| 8              | 1480656849 |            0 |       1 |
| 9              | 1480656849 |   1149794790 |       0 |
| 10             | 1480656849 |   1149792417 |       0 |
+----------------+------------+--------------+---------+
8 rows in set (0.02 sec)

В этом конкретном случае таблица achievement является "главной" таблицей и будет содержать информацию, которую я всегда хочу видеть. Таблица gamer_achievement содержит только информацию о достижениях, которые действительно получены. Для любой конкретной игры для любого конкретного геймера в таблице gamer_achievement может быть любое количество строк, в том числе нет, если за эту игру не было достигнуто никаких достижений. Например, в приведенных выше образцах данные о достижениях с идентификаторами 5, 6, 11 и 12 не были получены.

В настоящее время я написал

select a.id,
       a.name,
       ga.earned_epoch,
       ga.offline
from   achievement a 
       LEFT OUTER JOIN gamer_achievement ga 
       ON (a.id = ga.achievement_id and a.game_id = ga.game_id)
where  ga.gamer_id = 'fba8fcaa-f57b-44c6-9431-4ab78605b024' 
       and a.game_id = '1480656849'
order by convert (a.id, unsigned)

но это возвращает только полную информацию о тех достижениях, которые были действительно получены - информация о незаработанном достижении из правой таблицы (gamer_achievement) не отображается с помощью значений NULL, как я ожидал бы от этого типа запрос. Это то, что я ожидаю увидеть:

+----+-------------------------------+--------------+---------+
| id | name                          | earned_epoch | offline |
+----+-------------------------------+--------------+---------+
| 1  | Cluster Buster                |            0 |       1 |
| 2  | Star Gazer                    |            0 |       1 |
| 3  | Flower Child                  |            0 |       1 |
| 4  | Oyster-meister                |   1149789371 |       0 |
| 5  | Big Cheese of the South Seas  |         NULL |    NULL |
| 6  | Hexic Addict                  |         NULL |    NULL |
| 7  | Collapse Master               |   1149800406 |       0 |
| 8  | Survivalist                   |            0 |       1 |
| 9  | Tick-Tock Doc                 |   1149794790 |       0 |
| 10 | Marathon Mogul                |   1149792417 |       0 |
| 11 | Millionaire Extraordinaire    |         NULL |    NULL |
| 12 | Grand Pearl Pooh-Bah          |         NULL |    NULL |
+----+-------------------------------+--------------+---------+
12 rows in set (0.00 sec)

Что мне здесь не хватает? Из того, что я понимаю, основной запрос СМОТРЕТЬ мне, но я, очевидно, не хватает какой-то важной информации.

4b9b3361

Ответ 1

Многие ответили, но я тоже попробую и, надеюсь, одолжию еще кое-какие разъяснения. Как я всегда ее интерпретировал (и вы можете проверить так много других сообщений, на которые я ответил с помощью ЛЕВОГО присоединения), я пытаюсь перечислить таблицу, в которой я хочу все сначала (левая сторона... следовательно, читается слева направо). Затем слева присоединитесь к таблице "Другое" (справа) по всем критериям, находящимся между ними... Затем, когда вы выполняете левое соединение, и есть дополнительные критерии против правой таблицы, эти условия останутся с условием соединения, Введя их в предложение WHERE, будет подразумеваться INNER JOIN (всегда должен совпадать), который не тот, который вы хотите... Я также стараюсь всегда показывать левую таблицу alias.field = right table alias.field, чтобы сохранить корреляцию clear... Затем примените предложение where к базовым критериям, которые вы хотите от первой таблицы.. что-то вроде

select 
      a.id,
      a.name,
      ga.earned_epoch,
      ga.offline
   from   
      achievement a 
         LEFT OUTER JOIN gamer_achievement ga 
             ON a.id = ga.achievement_id
            AND a.game_id = ga.game_id
            AND ga.gamer_id = 'fba8fcaa-f57b-44c6-9431-4ab78605b024'
   where
      a.game_id = '1480656849'
   order by 
      convert (a.id, unsigned)

Обратите внимание на прямую связь между "a" и "ga" по общим идентификационным номерам ID и идентификатора игры, но затем привязана к определенному игроку. Предложение where заботится только о внешнем уровне достижения, основанном на конкретной игре.

Ответ 2

В предложении WHERE вы отбрасываете несколько строк, которые LEFT JOIN заполняли бы значениями NULL. Вы хотите поместить условие ga.gamer_id = 'fba8fcaa-f57b-44c6-9431-4ab78605b024' в предложение JOIN.

Другой вариант:

 LEFT OUTER JOIN (SELECT * FROM gamer_achievement
                   WHERE  ga.gamer_id = 'fba8fcaa-f57b-44c6-9431-4ab78605b024' 
                 ) ga 

Помните, что соединение выполняется, и в это время значения NULL появляются, если условие не может быть выполнено; то применяется фильтр where.

Ответ 3

WHERE Поля фильтров выводятся из всего набора результатов. Если вы хотите применить фильтр только к JOIN, вы можете добавить выражение в предложение ON.

В следующем запросе я переместил выражение фильтра, которое применяется к объединенной таблице (ga.gamer_id =) из предложения WHERE в предложение ON. Это предотвращает выделение выражения из строк, где значения gamer_achievement равны NULL.

SELECT a.id,
       a.name,
       ga.earned_epoch,
       ga.offline
FROM   achievement a 
       LEFT OUTER JOIN gamer_achievement ga 
       ON ga.achievement_id = a.id
       AND ga.game_id = a.game_id
       AND ga.gamer_id = 'fba8fcaa-f57b-44c6-9431-4ab78605b024'
WHERE
       a.game_id = '1480656849'
ORDER BY CONVERT(a.id, UNSIGNED)

Ответ 4

Это из-за этой строки:

where  ga.gamer_id = 'fba8fcaa-f57b-44c6-9431-4ab78605b024'

Если gamer не заработал achievement, значение ga.gamer_id будет NULL и не будет соответствовать условию WHERE.

Ответ 5

Я предполагаю, что предложение where отфильтровывает ваши желаемые результаты, перемещая его влево, может работать.

select a.id, 
       a.name, 
       ga.earned_epoch, 
       ga.offline 
from   achievement a  
       LEFT OUTER JOIN gamer_achievement ga  
       ON (a.id = ga.achievement_id and 
           a.game_id = ga.game_id and 
           ga.gamer_id = 'fba8fcaa-f57b-44c6-9431-4ab78605b024' and
           a.game_id = '1480656849') 
order by convert (a.id, unsigned)