guntersammet guntersammet - 1 month ago 4
MySQL Question

Left join in MySQL doesn't give me expected result

I have the following SQL:

SELECT t.teilnehmer_id, t.familienname, t.vorname, t.ort, t.ortsteil, t.kontrolle_ertrag, t.kontrolle_1j, t.kontrolle_brache,
SUM(fe.nutzflaeche) AS nutzflaeche_ertrag, GROUP_CONCAT(fe.nutzflaeche) AS einzelfl_ertrag,
SUM(fp.nutzflaeche) AS nutzflaeche_pflanzj, GROUP_CONCAT(fp.nutzflaeche) AS einzelfl_pflanzj,
SUM(fb.nutzflaeche) AS nutzflaeche_brache, GROUP_CONCAT(fb.nutzflaeche) AS einzelfl_brache,
SUM(fn.nutzflaeche) AS nutzflaeche_nicht_aush, GROUP_CONCAT(fn.nutzflaeche) AS einzelfl_nicht_aush
FROM teilnehmer t
LEFT JOIN anrede a ON (t.anrede_id = a.anrede_id)
LEFT JOIN antragsform af ON (t.antragsform_id = af.antragsform_id)
LEFT JOIN bank b ON (t.bank_id = b.bank_id)
LEFT JOIN flurverzeichnis fe ON (t.teilnehmer_id = fe.teilnehmer_id AND fe.kulturbez = 'E')
LEFT JOIN flurverzeichnis fp ON (t.teilnehmer_id = fp.teilnehmer_id AND fp.kulturbez = 'P')
LEFT JOIN flurverzeichnis fb ON (t.teilnehmer_id = fb.teilnehmer_id AND fb.kulturbez = 'B')
LEFT JOIN flurverzeichnis fn ON (t.teilnehmer_id = fn.teilnehmer_id AND fn.kulturbez = 'N')
WHERE 1 = 1
GROUP BY t.teilnehmer_id
ORDER BY familienname, vorname


The sum doesn't reflect the correct areas if there is a match in more than one kulturbez. E.g. if I have 5 rows with kulturbez 'E' and 2 rows with kulturbez 'N', each 'E' row shows up twice and each 'N' row shows up 5 times. Any suggestions on how to redo the SQL to only sum each row with the matching kulturbez once?
Thanks,

Gunter

Answer

As indicated in my comment, unavoidable 1:N joins usually need subqueries to calculate aggregate values appropriately; but it looks like your need can be solved with conditional aggregation, like so:

SELECT t.teilnehmer_id, t.familienname, t.vorname, t.ort, t.ortsteil, t.kontrolle_ertrag, t.kontrolle_1j, t.kontrolle_brache
    , SUM(CASE WHEN f.kulturbez = 'E' THEN f.nutzflaeche ELSE NULL END) AS nutzflaeche_ertrag
    , GROUP_CONCAT(CASE WHEN f.kulturbez = 'E' THEN f.nutzflaeche ELSE NULL END) AS einzelfl_ertrag
    , SUM(CASE WHEN f.kulturbez = 'P' THEN f.nutzflaeche ELSE NULL END) AS nutzflaeche_pflanzj
    , GROUP_CONCAT(CASE WHEN f.kulturbez = 'P' THEN f.nutzflaeche ELSE NULL END) AS einzelfl_pflanzj
    , SUM(CASE WHEN f.kulturbez = 'B' THEN f.nutzflaeche ELSE NULL END) AS nutzflaeche_brache
    , GROUP_CONCAT(CASE WHEN f.kulturbez = 'B' THEN f.nutzflaeche ELSE NULL END) AS einzelfl_brache
    , SUM(CASE WHEN f.kulturbez = 'N' THEN f.nutzflaeche ELSE NULL END) AS nutzflaeche_nicht_aush
    , GROUP_CONCAT(CASE WHEN f.kulturbez = 'N' THEN f.nutzflaeche ELSE NULL END) AS einzelfl_nicht_aush
FROM  teilnehmer t
    LEFT JOIN anrede a ON (t.anrede_id = a.anrede_id)
    LEFT JOIN antragsform af ON (t.antragsform_id = af.antragsform_id)
    LEFT JOIN bank b ON (t.bank_id = b.bank_id)
    LEFT JOIN flurverzeichnis f ON (t.teilnehmer_id = fe.teilnehmer_id)
WHERE 1 = 1
GROUP BY t.teilnehmer_id
ORDER BY familienname, vorname

Aggregate functions ignore NULL values for the most part. (Also, technically ELSE NULL is optional, as it is the assumed value if ELSE is not specified; but is good practice to make your intent clear.)