Izquierda Unirse con la cláusula Where

162

Necesito recuperar todas las configuraciones predeterminadas de la tabla de configuraciones pero también tomar la configuración de caracteres si existe para el carácter x.

Pero esta consulta solo recupera la configuración donde el carácter es = 1, no la configuración predeterminada si el usuario no ha configurado a nadie.

SELECT `settings`.*, `character_settings`.`value`
FROM (`settings`)
LEFT JOIN `character_settings` 
ON `character_settings`.`setting_id` = `settings`.`id`
WHERE `character_settings`.`character_id` = '1'  

Entonces debería necesitar algo como esto:

array(
    '0' => array('somekey' => 'keyname', 'value' => 'thevalue'),
    '1' => array('somekey2' => 'keyname2'),
    '2' => array('somekey3' => 'keyname3')
)

Donde la clave 1 y 2 son los valores predeterminados cuando la clave 0 contiene el valor predeterminado con el valor del carácter.

Sein Kraft
fuente

Respuestas:

345

La wherecláusula está filtrando las filas donde left joinno tiene éxito. Muévelo a la unión:

SELECT  `settings`.*, `character_settings`.`value`
FROM    `settings`
LEFT JOIN 
       `character_settings` 
ON     `character_settings`.`setting_id` = `settings`.`id`
        AND `character_settings`.`character_id` = '1'  
Andomar
fuente
55

Al hacer OUTER JOIN (ANSI-89 o ANSI-92), la ubicación de la filtración es importante porque los criterios especificados en la ONcláusula se aplican antes de que se realice el JOIN . Los criterios contra una tabla OUTER JOINed proporcionada en la WHEREcláusula se aplican después de que se realiza JOIN . Esto puede producir conjuntos de resultados muy diferentes. En comparación, no importa para INNER JOINs si el criterio se proporciona en las cláusulas ONo WHERE, el resultado será el mismo.

  SELECT  s.*, 
          cs.`value`
     FROM SETTINGS s
LEFT JOIN CHARACTER_SETTINGS cs ON cs.setting_id = s.id
                               AND cs.character_id = 1
Ponis OMG
fuente
21

Si entiendo su pregunta correctamente, desea registros de la base de datos de configuración si no tienen una unión en la tabla character_settings o si ese registro unido tiene character_id = 1.

Por lo tanto, debes hacer

SELECT `settings`.*, `character_settings`.`value`
FROM (`settings`)
LEFT OUTER JOIN `character_settings` 
ON `character_settings`.`setting_id` = `settings`.`id`
WHERE `character_settings`.`character_id` = '1' OR
`character_settings`.character_id is NULL
marca
fuente
77
Que los riesgos de volver falsos positivos
OMG potros
1
@OMGPonies No entiendo, ¿Cuáles podrían ser los casos en que tiene riesgos? En mi caso, apliqué el AND con join y no hubo resultados, pero cuando utilicé la solución anterior, obtuve resultados. Pero quiero estar seguro de los problemas que puedo enfrentar con esta opción.
Chetan Sharma
2

Puede que le resulte más fácil de entender utilizando una simple subconsulta

SELECT `settings`.*, (
    SELECT `value` FROM `character_settings`
    WHERE `character_settings`.`setting_id` = `settings`.`id`
      AND `character_settings`.`character_id` = '1') AS cv_value
FROM `settings`

La subconsulta puede devolver nulo, por lo que no tiene que preocuparse por JOIN / WHERE en la consulta principal.

A veces, esto funciona más rápido en MySQL, pero compárelo con el formulario LEFT JOIN para ver qué funciona mejor para usted.

SELECT s.*, c.value
FROM settings s
LEFT JOIN character_settings c ON c.setting_id = s.id AND c.character_id = '1'
RichardTheKiwi
fuente
1

El resultado es correcto según la instrucción SQL. La unión izquierda devuelve todos los valores de la tabla derecha y solo los valores coincidentes de la tabla izquierda.

Las columnas ID y NAME son de la tabla del lado derecho, por lo que se devuelven.

La puntuación es de la tabla de la izquierda y se devuelve 30, ya que este valor se relaciona con el Nombre "Flow". Los otros nombres son NULL ya que no se relacionan con el Nombre "Flow".

Lo siguiente devolverá el resultado que esperaba:

    SELECT  a.*, b.Score
FROM    @Table1 a
    LEFT JOIN @Table2 b
       ON a.ID = b.T1_ID 
WHERE 1=1
AND a.Name = 'Flow'

El SQL aplica un filtro en la tabla de la derecha.

Dan
fuente
0

Para este problema, como para muchos otros que involucran uniones izquierdas no triviales como la unión izquierda en tablas unidas internamente, encuentro conveniente y algo más legible dividir la consulta con una withcláusula. En tu ejemplo,

with settings_for_char as (
  select setting_id, value from character_settings where character_id = 1
)
select
  settings.*,
  settings_for_char.value
from
  settings
  left join settings_for_char on settings_for_char.setting_id = settings.id;
P-Gn
fuente