Links Join With Where-Klausel


162

Ich muss alle Standardeinstellungen aus der Einstellungstabelle abrufen, aber auch die Zeicheneinstellung abrufen, falls für x Zeichen vorhanden.

Diese Abfrage ruft jedoch nur die Einstellungen ab, bei denen das Zeichen = 1 ist, nicht die Standardeinstellungen, wenn der Benutzer niemanden festgelegt hat.

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

Also sollte ich so etwas brauchen:

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

Wobei Schlüssel 1 und 2 die Standardwerte sind, wenn Schlüssel 0 den Standardwert mit dem Zeichenwert enthält.

Antworten:


344

Die whereKlausel filtert Zeilen weg, bei denen left joindies nicht erfolgreich ist. Verschieben Sie es zum 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

Bei der Erstellung von OUTER JOINs (ANSI-89 oder ANSI-92) ist der Filterort von Bedeutung, da die in der ONKlausel angegebenen Kriterien angewendet werden, bevor die JOIN durchgeführt wird . Kriterien für eine in der WHEREKlausel angegebene Tabelle OUTER JOINed werden angewendet, nachdem JOIN erstellt wurde . Dies kann zu sehr unterschiedlichen Ergebnismengen führen. Im Vergleich dazu spielt es für INNER JOINs keine Rolle, ob die Kriterien in den Klauseln ONoder angegeben WHEREsind - das Ergebnis ist dasselbe.

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

21

Wenn ich Ihre Frage richtig verstehe, möchten Sie Datensätze aus der Einstellungsdatenbank, wenn sie keinen Join für die Tabelle character_settings haben oder wenn dieser verknüpfte Datensatz Character_id = 1 hat.

Sie sollten daher tun

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
Das könnte zu Fehlalarmen führen
OMG Ponies

1
@OMGPonies Ich verstehe nicht, Was könnte der Fall sein, dass es Risiken gibt. In meinem Fall habe ich das UND mit Join angewendet und es war kein Ergebnis vorhanden, aber als ich die obige Lösung verwendet habe, hatte ich Ergebnisse. Aber ich möchte sicher sein, welche Probleme ich mit dieser Option haben kann.
Chetan Sharma

2

Möglicherweise ist es einfacher zu verstehen, wenn Sie eine einfache Unterabfrage verwenden

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`

Die Unterabfrage darf null zurückgeben, sodass Sie sich in der Hauptabfrage keine Gedanken über JOIN / WHERE machen müssen.

Manchmal funktioniert dies in MySQL schneller , aber vergleichen Sie es mit dem LEFT JOIN-Formular, um zu sehen, was für Sie am besten funktioniert.

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

1

Das Ergebnis ist basierend auf der SQL-Anweisung korrekt. Der linke Join gibt alle Werte aus der rechten Tabelle und nur übereinstimmende Werte aus der linken Tabelle zurück.

ID- und NAME-Spalten stammen aus der rechten Seitentabelle und werden daher zurückgegeben.

Die Punktzahl stammt aus der linken Tabelle und 30 wird zurückgegeben, da sich dieser Wert auf den Namen "Flow" bezieht. Die anderen Namen sind NULL, da sie sich nicht auf den Namen "Flow" beziehen.

Das Folgende würde das erwartete Ergebnis zurückgeben:

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

Die SQL wendet einen Filter auf die rechte Tabelle an.


0

Für dieses Problem, wie für viele andere, die nicht triviale Linksverknüpfungen beinhalten, wie z. B. Linksverknüpfungen in Tabellen mit inneren Verknüpfungen, finde ich es bequemer und etwas lesbarer, die Abfrage mit einer withKlausel zu teilen . In Ihrem Beispiel

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;
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.