[lug-ld] SQL Frage

Christian Boltz lug-ld at cboltz.de
Do Sep 25 21:53:39 CEST 2014


Hallo Ekki, hallo zusammen,

Am Dienstag, 23. September 2014 schrieb Ekki Plicht:
> Gegeben sei eine Tabelle mit Datum, Land, Umsatz. Durch eine simple
> Abfrage bekomme ich die Top 3 umsatzstärksten Länder über den
> gesamten Zeitraum raus:
> 
> http://sqlfiddle.com/#!2/789b8a/3/0
> 
> Das ist aber nicht das Ziel, vielmehr möchte ich haben:
> Eine Liste aller Top 3 Länder über die Zeit (je Monat), sortiert nach
> Monat, dann Umsatz.
>
> Ich kann natürlich die Top 3 Länder auslesen und manuell in die WHERE
> clause einbauen, aber das ist ja langweilig. Die Top 3 können sich ja
> auch mal ändern, darum soll das Resultat der ersten Abfrage (Wer sind
> die Top 3 heute?) als WHERE clause in die zweite Abfrage einfliessen.

Du willst also für die _heutigen_ Top 3 alle Daten der Vormonate.

Das ist schonmal angenehmer als die Top 3 des jeweiligen Monats ;-)

> Ich weiss das ich die erste Abfrage noch anpassen muss, damit pro
> Datensatz nur das Land zurückgeliefert wird.
> 
> Ich weiss das es sowas wie Subqueries gibt, habe damit aber noch nie
> so recht Erfolg gehabt. Oder ich bastele mir programmatisch eine
> WHERE land=? OR land=? or land=? Abfrage...
> Aber ich hätte gerne gelernt wie ich das nur mit SQL hinbekomme.

Ohne jetzt Deine genaue Query einzubauen:

SELECT x, y FROM table 
WHERE land IN (SELECT land FROM othertable WHERE ...)


Im Ergebnis brauchst Du theoretisch sowas wie:

select `land`, sum(`umsatz`), month(`date`), year(`date`) FROM `umsatz`
WHERE `land` IN 
(
SELECT `land` FROM `umsatz` GROUP BY `land` 
                   ORDER BY SUM(`umsatz`) DESC LIMIT 0,3
)
GROUP BY month(`date`), year(`date`), `land`

Praktisch kommt allerdings folgendes Ergebnis bei sqlfiddle:

This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME 
subquery'

Nice[tm].

Du musst also die Query zur Ermittlung der Top 3 separat laufen lassen 
und in der zweiten Query einbauen:

SELECT `land`, sum(`umsatz`), month(`date`), year(`date`) FROM `umsatz`
WHERE `land` IN ('AT', 'DE', 'NO') 
GROUP BY month(`date`), year(`date`), `land`

> Bonus-Komplikation:
> Nun möchte ich eine Liste wie oben, also die Umsätze der Top3-Länder
> über die Monate, aber den Umsatz aller anderen Länder (Umsatzrang 4
> bis unendlich) zusammengefasst als 'Rest_of_World' haben.
>
> Geht das auch noch mit reinem SQL? Oder packe ich sowas lieber in eine
> SP? Oder mache das besser im umgebenden Programm (da ist es leicht).

WHERE land NOT IN ('foo', 'bar', 'baz')


Wenn man das alles zusammenbastelt, kommt man auf:

select `land`, sum(`umsatz`), month(`date`) monat, year(`date`) jahr 
FROM `umsatz`
WHERE `land` IN ('AT', 'DE', 'NO') 
GROUP BY month(`date`), year(`date`), `land`

UNION

select 'zz_other' as `land`, sum(`umsatz`), month(`date`) monat, 
year(`date`) jahr FROM `umsatz`
WHERE `land` NOT IN ('AT', 'DE', 'NO') 
GROUP BY month(`date`), year(`date`)             <----- nicht: `land`

ORDER BY `jahr`, `monat`, `land`

Das besteht zwar rein technisch aus zwei Queries ("UNION"), als Ergebnis 
bekommst Du aber ein Ergebnis mit allen gewünschten Daten.


Die Spalten-Aliase "monat" und "jahr" solltest Du verwenden, weil Du 
ansonsten ein quasi unlesbares

ORDER BY 'month(`date`)', 'year(`date`)', `land`

brauchst - und wehe, Du hast irgendwo falsche Anführungszeichen ;-)


Gruß

Christian Boltz
-- 
>> MCSE: "Microsoft Certified Stupidity enclosed"  [A. Spengler in dasr]
> Ich dachte das heißt: 
> MCSE - Must Call Somebody Else          [Markus Feilner in suse-linux]
Na Na Na!!! Ihr könnt doch nicht so einfach über so ein Zertifikat
herziehen! Das ist bestimmt der Neid der Besitzlosen! Wenn ich hier an
die Wand sehe (da hängt das bei mir), dann lese ich ganz deutlich:
    Minsweeper Consultant and Solitaire Expert
Und darauf lege ich doch grossen Wert!!!! [Konrad Neitzel in suse-linux]