Klauzula Left Join With Where

162

Muszę pobrać wszystkie domyślne ustawienia z tabeli ustawień, ale także pobrać ustawienie znaku, jeśli istnieje dla znaku x.

Ale to zapytanie pobiera tylko te ustawienia, w których znak jest = 1, a nie ustawienia domyślne, jeśli użytkownik nikogo nie ustawił.

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

Powinienem więc potrzebować czegoś takiego:

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

Gdzie klucz 1 i 2 są wartościami domyślnymi, gdy klucz 0 zawiera wartość domyślną z wartością znakową.

Sein Kraft
źródło

Odpowiedzi:

346

whereKlauzula jest filtrowanie dala wiersze, gdzie left joinnie uda. Przenieś to do złączenia:

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
źródło
55

Podczas wykonywania ZŁĄCZEŃ ZEWNĘTRZNYCH (ANSI-89 lub ANSI-92), lokalizacja filtracji ma znaczenie, ponieważ kryteria określone w ONklauzuli są stosowane przed wykonaniem JOIN . Kryteria dotyczące tabeli OUTER JOINed podane w WHEREklauzuli są stosowane po wykonaniu JOIN . Może to dawać bardzo różne zestawy wyników. Dla porównania, w przypadku INNER JOINs nie ma znaczenia, czy kryteria są podane w klauzulach ONlub WHERE- wynik będzie taki sam.

  SELECT  s.*, 
          cs.`value`
     FROM SETTINGS s
LEFT JOIN CHARACTER_SETTINGS cs ON cs.setting_id = s.id
                               AND cs.character_id = 1
Kucyki OMG
źródło
21

Jeśli dobrze rozumiem twoje pytanie, potrzebujesz rekordów z bazy danych ustawień, jeśli nie mają one złączenia w tabeli character_settings lub jeśli ten dołączony rekord ma znak_id = 1.

Dlatego powinieneś to zrobić

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
znak
źródło
7
To ryzykuje zwróceniem fałszywych alarmów
kucyki OMG
1
@OMGPonies Nie rozumiem, jakie mogą być przypadki, że ma ryzyko. W moim przypadku zastosowałem AND z łączeniem i nie było wyniku, ale kiedy użyłem powyższego rozwiązania, miałem wyniki. Ale chcę mieć pewność, z jakimi problemami mogę się zmierzyć dzięki tej opcji.
Chetan Sharma
2

Może być łatwiejsze do zrozumienia, używając prostego podzapytania

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`

Podzapytanie może zwrócić wartość null, więc nie musisz się martwić o JOIN / WHERE w głównym zapytaniu.

Czasami działa to szybciej w MySQL, ale porównaj to z formularzem LEFT JOIN, aby zobaczyć, co działa najlepiej dla Ciebie.

SELECT s.*, c.value
FROM settings s
LEFT JOIN character_settings c ON c.setting_id = s.id AND c.character_id = '1'
RichardTheKiwi
źródło
1

Wynik jest poprawny na podstawie instrukcji SQL. Lewe sprzężenie zwraca wszystkie wartości z prawej tabeli i tylko pasujące wartości z lewej tabeli.

Kolumny ID i NAZWA pochodzą z tabeli po prawej stronie, więc są zwracane.

Wynik pochodzi z lewej tabeli i zwracane jest 30, ponieważ ta wartość odnosi się do nazwy „Flow”. Pozostałe nazwy mają wartość NULL, ponieważ nie odnoszą się do nazwy „Flow”.

Poniższe zwróci oczekiwany wynik:

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

SQL stosuje filtr do prawej tabeli.

Dan
źródło
0

W przypadku tego problemu, podobnie jak w przypadku wielu innych związanych z nietrywialnymi łączeniami lewostronnymi, takimi jak łączenie lewostronne w tabelach połączonych wewnętrznie, wygodniejsze i nieco bardziej czytelne jest podzielenie zapytania za pomocą withklauzuli. W twoim przykładzie

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
źródło