tablas - mysql union group by
Problemas para obtener la UNIÓN EXTERIOR IZQUIERDA para trabajar (5)
Creí entender cómo funcionan las uniones externas izquierdas, pero tengo una situación que no funciona, y no estoy 100% seguro de si la forma en que mi consulta está estructurada es incorrecta o si se trata de un problema de datos.
Para el fondo, tengo las siguientes estructuras de tabla 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)
y
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)
En cuanto a los datos, esto es lo que he poblado aquí (solo se incluyen columnas pertinentes por brevedad):
+----+------------+------------------------------+
| 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)
y
+----------------+------------+--------------+---------+
| 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)
En este caso particular, la tabla de achievement
es la tabla "maestra" y contendrá la información que siempre quiero ver. La tabla gamer_achievement
solo contiene información de los logros que se obtienen realmente. Para cualquier juego en particular para cualquier jugador en particular, puede haber cualquier cantidad de filas en la tabla gamer_achievement
, incluyendo ninguna si no se han obtenido logros para ese juego. Por ejemplo, en los datos de muestra anteriores, los logros con los identificadores 5, 6, 11 y 12 no se han obtenido.
Lo que actualmente escribí es
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)
pero esto solo devuelve la información completa de los logros que se han obtenido realmente: la información de logro no obtenida de la tabla de la derecha ( gamer_achievement
) no se muestra con los valores NULL como esperaría de este tipo de consulta. Esto es lo que espero ver:
+----+-------------------------------+--------------+---------+
| 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)
¿Que me estoy perdiendo aqui? Por lo que entiendo, la consulta básica ME PARECE correcta, pero obviamente me falta algo de información crítica.
En la cláusula WHERE, descarta algunas filas que LEFT JOIN habría rellenado con valores NULL. Desea poner la condición ga.gamer_id = ''fba8fcaa-f57b-44c6-9431-4ab78605b024''
dentro de la cláusula JOIN.
Otra opción es:
LEFT OUTER JOIN (SELECT * FROM gamer_achievement
WHERE ga.gamer_id = ''fba8fcaa-f57b-44c6-9431-4ab78605b024''
) ga
Recuerde que la unión se realiza, y en este momento, los valores NULL vienen si la condición no se puede cumplir; entonces el filtro where
aplica.
Es por esta línea:
where ga.gamer_id = ''fba8fcaa-f57b-44c6-9431-4ab78605b024''
Si el gamer
no ha obtenido el achievement
, el valor ga.gamer_id
será NULL
y no calificará para la condición WHERE
.
Muchos han respondido, pero lo intentaré también y, con un poco de suerte, prestaré más aclaraciones. Cómo lo he interpretado siempre (y puede consultar tantas otras publicaciones a las que he respondido con combinaciones LEFT), trato de hacer una lista de la tabla en la que quiero todo desde el principio (lado izquierdo ... por lo tanto, leo de izquierda a derecha). Luego, únete a la tabla "Otro" (lado derecho) en cualquier criterio que esté entre ellos ... Luego, al hacer una combinación izquierda, y hay criterios adicionales en la tabla lateral derecha, esas condiciones se mantendrían con esa condición de unión . Al incluirlos en la cláusula "WHERE" implicaría una unión INNER (siempre debe coincidir) que no es lo que desea ... También trato de mostrar siempre la tabla izquierda alias.field = right table alias.field para mantener la correlación claro ... Luego, aplique la cláusula where a los criterios básicos que desee de la primera tabla ... algo así como
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)
Observe la relación directa entre "a" y "ga" según la ID común y los valores de ID del juego, pero luego virada en el jugador específico. La cláusula where solo se preocupa por el nivel de logro externo en función del juego específico.
Supongo que la cláusula where está filtrando los resultados deseados, moverlo a la combinación izquierda puede funcionar.
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)
WHERE
cláusulas WHERE
filtran los resultados de todo el conjunto de resultados. Si desea aplicar un filtro solo a JOIN
, puede agregar la expresión a la cláusula ON
.
En la siguiente consulta, he movido la expresión de filtro que se aplica a la tabla unida ( ga.gamer_id =
) de la cláusula WHERE a la cláusula ON. Esto evita que la expresión filtre las filas donde los valores de gamer_achievement son 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)