Sinistra Unisciti alla clausola Where


162

Devo recuperare tutte le impostazioni predefinite dalla tabella delle impostazioni, ma anche prendere l'impostazione del carattere se esiste per il carattere x.

Ma questa query recupera solo quelle impostazioni in cui il carattere è = 1, non le impostazioni predefinite se l'utente non ha impostato nessuno.

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

Quindi dovrei aver bisogno di qualcosa del genere:

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

Dove chiave 1 e 2 sono i valori predefiniti quando la chiave 0 contiene il valore predefinito con il valore del carattere.

Risposte:


346

La whereclausola sta filtrando le righe in cui left joinnon riesce. Spostalo sul join:

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

55

Quando si effettuano JOU ESTERNI (ANSI-89 o ANSI-92), la posizione di filtraggio è importante perché i criteri specificati nella ONclausola vengono applicati prima che venga creato JOIN . I criteri rispetto a una tabella OUTER JOIN fornita nella WHEREclausola vengono applicati dopo aver effettuato l' adesione . Ciò può produrre set di risultati molto diversi. In confronto, non importa per INNER JOINs se i criteri sono forniti nelle clausole ONo WHERE- il risultato sarà lo stesso.

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

21

Se capisco correttamente la tua domanda, vuoi i record dal database delle impostazioni se non hanno un join nella tabella character_settings o se quel record unito ha character_id = 1.

Dovresti quindi farlo

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

7
Ciò rischia di restituire falsi positivi
OMG Pony il

1
@OMGPonies Non capisco, quali potrebbero essere i casi in cui ha dei rischi. Nel mio caso ho applicato l'AND con join e non c'erano risultati, ma quando ho usato la soluzione sopra, ho avuto risultati. Ma voglio essere sicuro dei problemi che posso affrontare con questa opzione.
Chetan Sharma,

2

Potresti trovare più facile capire usando una semplice subquery

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 subquery può restituire null, quindi non devi preoccuparti di JOIN / WHERE nella query principale.

A volte, questo funziona più velocemente in MySQL, ma confrontalo con il modulo LEFT JOIN per vedere cosa funziona meglio per te.

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

1

Il risultato è corretto in base all'istruzione SQL. L'unione sinistra restituisce tutti i valori dalla tabella destra e solo i valori corrispondenti dalla tabella sinistra.

Le colonne ID e NAME provengono dalla tabella laterale destra, quindi vengono restituite.

Il punteggio è dalla tabella di sinistra e viene restituito 30, poiché questo valore si riferisce al nome "Flusso". Gli altri nomi sono NULL in quanto non si riferiscono al nome "Flusso".

Quanto segue restituisce il risultato che ti aspettavi:

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

L'SQL applica un filtro sulla tabella a destra.


0

Per questo problema, come per molti altri che coinvolgono join di sinistra non banali come join di sinistra su tabelle con join interno, trovo conveniente e un po 'più leggibile dividere la query con una withclausola. Nel tuo esempio,

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;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.