让LEFT OUTER JOIN工作的问题

时间:2012-01-25 13:19:03

标签: mysql sql join outer-join

我以为我理解左外连接是如何工作的,但是我的情况不起作用,而且我不能100%确定我的查询结构是否不正确,或者是否是数据问题。< / p>

对于后台,我有以下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表中可以有任意数量的行 - 如果没有为该游戏获得任何成就,则包括无行数。例如,在上面的示例数据中,尚未获得ID为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)

我在这里缺少什么?根据我的理解,基本查询对我而言,但我显然错过了一些关键信息。

5 个答案:

答案 0 :(得分:16)

许多人已经回答了,但我也会尝试,希望能提供更多的澄清。我如何总是解释它(你可以检查我用LEFT连接回复的许多其他帖子),我尝试列出我想要的所有内容的表(左侧...因此从左到右阅读)。然后左边加入“其他”表(右侧),无论它们之间的条件是什么......然后,当进行左连接时,对于右侧表还有其他标准,这些条件将保持该连接条件。通过将它们带入“WHERE”子句意味着INNER JOIN(必须始终匹配)这不是你想要的...我也尝试始终显示左表alias.field =右表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)

通过公共ID和游戏ID值注意“a”和“ga”之间的直接关系,但随后在特定游戏玩家上加以说明。 where子句仅关注基于特定游戏的外部成就水平。

答案 1 :(得分:9)

在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过滤器。

答案 2 :(得分: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)

答案 3 :(得分:1)

这是因为这一行:

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

如果gamer未获得achievement,则ga.gamer_id值将为NULL,且不符合WHERE条件。

答案 4 :(得分:0)

我的猜测是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)