# SQL - Mehrfacheinträge nach Häufigkeit ausgeben



## grünes-huhn (13. Juli 2009)

Hi,

ich hätte da mal eine Tabelle, gefüllt mit Produkten.

Nun möchte ich quasi die meistgekauften auslesen.

So sieht die Tabelle in etwa aus:

id          Produktname
1           Holla
2           Holla
3           Servus
4           Tach
5           Tach
6           Tach

Ergebnis hätt ich gerne folgendes:

Tach
Holla
Servus

eben auch sortiert nach letzten Eintrag, also Id (zb wenn ich Limit 4 hätte: Tach und Servus)

im Moment hätt ich das hier:
SELECT * FROM orderedprod GROUP by name HAVING COUNT(name)>2 ORDER by oprod_orderid DESC LIMIT 30

Das ist natürlich irgendwie Blödsinn.....

Danke schön mal für eure Gedanken


----------



## kuddeldaddeldu (13. Juli 2009)

Hi,

spontan würde ich sagen:


```
select name, count(id) as c 
   from tabelle
   group by name
   order by c desc
```

Allerdings:



grünes-huhn hat gesagt.:


> eben auch sortiert nach letzten Eintrag, also Id (*zb wenn ich Limit 4 hätte: Tach und Servus*)



Hä? Die Logik verstehe ich nicht...
Entweder willst Du die 4 meistgekauften oder die 4 zuletzt gekauften Produkte...

LG


----------



## grünes-huhn (14. Juli 2009)

kuddeldaddeldu hat gesagt.:


> Hi,
> 
> spontan würde ich sagen:
> 
> ...



Naja ich würde gerne die meistverkauften Produkte der letzten 2 Monate ausgeben, damit sie immer aktuell sind.


soweit, dass ich die meistverkauften Produkte ausgebe, bin ich schon. Nur wenn ich es sortiere, gehts entweder nach Produkten oder nach ID. 
Das hab ich jetzt:

SELECT *, COUNT(name) as hugo FROM orderedprod GROUP by name  ORDER by hugo DESC  LIMIT 9;

Muss ich da mit zwei abfragen arbeiten? Das wollte ich mir eigentlich ersparen!
Also ich müsste das tun: "ORDER by id, hugo"  nur geht das so nicht ganz.


----------



## Steiner_B (14. Juli 2009)

grünes-huhn hat gesagt.:


> Naja ich würde gerne die meistverkauften Produkte der letzten 2 Monate ausgeben, damit sie immer aktuell sind.
> .



Es gibt dann demnach eine Datumspalte in der Tabelle?


```
SELECT name, COUNT(id) AS c
FROM tabelle
GROUP BY name HAVING datum > TO_DATE(vor_zwei_monaten)
ORDER BY c DESC
```
du kannst natürlich zusätzlich noch nach z.B.: dem Datum des letzen Verkaufs ordnen mit 

```
ORDER BY c DESC, MAX(datum)
```


----------



## grünes-huhn (14. Juli 2009)

Steiner_B hat gesagt.:


> *Es gibt dann demnach eine Datumspalte in der Tabelle?
> *
> 
> ```
> ...




nein ich mach es nach ID. Die zwei Monate waren nur ein Beispiel. etwa ID Limit 50 oder sowas, aber eben nach ID sortiert DESC


----------



## Steiner_B (14. Juli 2009)

Du willst die z.B.: 50 letzten Einträge in der Tabelle? und deine IDs sind autowerte?


```
SELECT name, COUNT(id) as c FROM
   (SELECT name, id FROM tabelle ORDER BY id LIMIT 50)
   GROUP BY name
   ORDER BY c DESC
```

ev. musst du noch bei ORDER BY id ein DESC dahinter schreiben, ich hab grad keine DB zum probieren


----------



## grünes-huhn (14. Juli 2009)

Steiner_B hat gesagt.:


> Du willst die z.B.: 50 letzten Einträge in der Tabelle? und deine IDs sind autowerte?
> 
> 
> ```
> ...



ich habs jetzt noch nicht getestet, aber so les ich nur die letzten 50 Einträge aus und nicht die mit den meisten Produkten.

Ich brauch die letzten 50 Einträge mit den meisten gleichen Datensätzen.


----------



## kuddeldaddeldu (14. Juli 2009)

Hi,



grünes-huhn hat gesagt.:


> ich habs jetzt noch nicht getestet, aber so les ich nur die letzten 50 Einträge aus und nicht die mit den meisten Produkten.



Du hast übersehen, dass das eine Subquery ist. Erst werden 50 Datensätze ausgelesen. Diese werden dann nach Name gruppiert und absteigend nach Anzahl Datensätze pro Gruppe sortiert. Genau das, was Du haben wolltest. Nur die Sortierfolge bei der Unterabfrage fehlt noch:


```
SELECT name, COUNT(id) AS c FROM
   (SELECT name, id FROM tabelle ORDER BY id DESC LIMIT 50)
   GROUP BY name
   ORDER BY c DESC
```

LG


----------



## Steiner_B (14. Juli 2009)

Du willst also eine List von Namen, sortiert nach der Anzahl ihrer Vorkommen, und davon dann die 50 mit den meisten Einträgen? Hab ich das jetzt richtig mitbekommen? 

Warum willst du dann nach der ID sortieren? Es gibt ja immer mehrere IDs zu jedem Namen, nach welcher wolltest du dann überhaupt sortieren?


----------



## grünes-huhn (14. Juli 2009)

kuddeldaddeldu hat gesagt.:


> Hi,
> 
> 
> 
> ...



ich habs nun probiert und bekomm immer nen Error:


```
SELECT name, COUNT(id) as c FROM (SELECT name, id FROM orderedprod ORDER by id DESC LIMIT 50)  GROUP by name ORDER by c DESC
```


----------



## Steiner_B (14. Juli 2009)

Bitte den Fehler auch posten, sonst wirds schwer.

Ganz andere Frage: Welche Datenbank steckt da überhaupt dahinter?


----------



## grünes-huhn (14. Juli 2009)

Steiner_B hat gesagt.:


> Du willst also eine List von Namen, sortiert nach der Anzahl ihrer Vorkommen, und davon dann die 50 mit den meisten Einträgen? Hab ich das jetzt richtig mitbekommen?
> 
> Warum willst du dann nach der ID sortieren? Es gibt ja immer mehrere IDs zu jedem Namen, nach welcher wolltest du dann überhaupt sortieren?




ich will von meinen verkauften Produkten die meistverkauften rausfiltern, jedoch nur der letzten 50 Id´s. War das verständlich?

Also ich möchte zb. nicht das ein Produkt ewig an erster Stelle liegt, sondern eben nur zum aktuellen Zeitpunkt ein gut verkauftes Produkt.


----------



## kuddeldaddeldu (14. Juli 2009)

Hi,



grünes-huhn hat gesagt.:


> ich habs nun probiert und bekomm immer nen Error:



dann solltest Du auch den Fehler posten und nicht die Abfrage, die kennen wir doch schon...

Ich orakel mal einen "every derived table must have its own alias" oder so ähnlich.

Also gibst Du der Subquery einfach mal einen Alias:


```
SELECT name, COUNT(id) AS c FROM
   (SELECT name, id FROM tabelle ORDER BY id DESC LIMIT 50) t
   GROUP BY name
   ORDER BY c DESC
```

LG


----------



## grünes-huhn (14. Juli 2009)

kuddeldaddeldu hat gesagt.:


> Hi,
> 
> 
> 
> ...



ein Stinknormaler Fehler: supplied argument is not a valid MySQL result resource

Also, als wär die syntax in der Select Abfrage falsch oder der Tabellenname.


----------



## kuddeldaddeldu (14. Juli 2009)

Hi,



grünes-huhn hat gesagt.:


> ein Stinknormaler Fehler: supplied argument is not a valid MySQL result resource



das ist doch nur ein Folgefehler, da die Abfrage fehlgeschlagen ist. Die MySQL-Fehlermeldung bekommst Du mit [phpf]mysql_error[/phpf].

LG


----------



## grünes-huhn (14. Juli 2009)

kuddeldaddeldu hat gesagt.:


> Hi,
> 
> 
> 
> ...




ja, steht das nun:
"Für jede abgeleitete Tabelle muss ein eigener Alias angegeben werden"

ich probiers mal durch, mit deinem Vorschlag....


----------



## grünes-huhn (14. Juli 2009)

So funktioniert es jetzt!!

Kannst du mir erklären was diese t macht? ISt mir ehrlich gesagt nicht ganz klar.


----------



## kuddeldaddeldu (14. Juli 2009)

Hi,



grünes-huhn hat gesagt.:


> Kannst du mir erklären was diese t macht? ISt mir ehrlich gesagt nicht ganz klar.



dann lies mal den entsprechenden Abschnitt im Handbuch. Das ist wie gesagt ein Alias.

LG


----------



## grünes-huhn (14. Juli 2009)

kuddeldaddeldu hat gesagt.:


> Hi,
> 
> 
> 
> ...



ah danke, alles klar. 

Ein anliegen hätt ich noch....
Kann ich eventuell 50 Datensätze abfragen aber nur wo c>1 ist? (Also nur Produkte, wo mehr als eines Verkauft wurde)
Also etwas so:

```
SELECT *, COUNT(id) as c 
FROM (SELECT * FROM orderedprod 
ORDER by id DESC LIMIT 50)t  
WHERE c>1
GROUP by name ORDER by c
```


----------



## kuddeldaddeldu (14. Juli 2009)

Hi,

das macht man nicht mit WHERE sondern mit einer HAVING-Kausel. Aber ehrlich mal, erweitere Deine Tabelle um einen Timestamp! Das wird doch nur Murks so. Wenn mal ein Produkt durch eine Werbeaktion 200 Mal innerhalb kürzerer Zeit bestellt wird, liefert Deine Abfrage mit etwas Pech (die 50 letzten Einträge gehören alle zu diesem Produkt) auch nur dieses zurück. Wenn Du einen Timestamp in der Tabelle hast, kannst Du eine einfache gruppierte Abfrage (ohne Subquery) mit Einschränkung auf meinetwegen die letzten 2 Monate schreiben und dafür eine LIMIT-Klausel setzen. Dann bekommst Du auch wirklich genau die 50 beliebtesten Produkte der letzten 2 Monate (falls in diesem Zeitraum so viel verschiedene bestellt wurden).

LG


----------



## grünes-huhn (14. Juli 2009)

kuddeldaddeldu hat gesagt.:


> Hi,
> 
> das macht man nicht mit WHERE sondern mit einer HAVING-Kausel. Aber ehrlich mal, erweitere Deine Tabelle um einen Timestamp! Das wird doch nur Murks so. Wenn mal ein Produkt durch eine Werbeaktion 200 Mal innerhalb kürzerer Zeit bestellt wird, liefert Deine Abfrage mit etwas Pech (die 50 letzten Einträge gehören alle zu diesem Produkt) auch nur dieses zurück. Wenn Du einen Timestamp in der Tabelle hast, kannst Du eine einfache gruppierte Abfrage (ohne Subquery) mit Einschränkung auf meinetwegen die letzten 2 Monate schreiben und dafür eine LIMIT-Klausel setzen. Dann bekommst Du auch wirklich genau die 50 beliebtesten Produkte der letzten 2 Monate (falls in diesem Zeitraum so viel verschiedene bestellt wurden).
> 
> LG




danke!

den Timestamp gibts in einer anderen Tabelle, in der "Bestelltabelle". Die Produkte haben eine Bestell-ID, welche die ID der Bestelltabelle ist. 
Ich wollt es mir ehrlich gesagt einfach machen :-(


----------



## Steiner_B (14. Juli 2009)

Mach einen Join über beide Tabellen, dann brauchst du eh fast nichts ändern am vorhergehenen Statement


----------



## grünes-huhn (14. Juli 2009)

Steiner_B hat gesagt.:


> Mach einen Join über beide Tabellen, dann brauchst du eh fast nichts ändern am vorhergehenen Statement




ehrlich..... Ich blick bei Join, Inner Join, Left usw. nicht so durch:-(

obwohl ich mir sicher einiges damit ersparen würde, würde ich mich da mal einlesen


----------



## kuddeldaddeldu (14. Juli 2009)

Hi,



grünes-huhn hat gesagt.:


> ehrlich..... Ich blick bei Join, Inner Join, Left usw. nicht so durch:-(



das lässt sich ändern:

Einführung in Joins
Fortgeschrittene Jointechniken

Du solltest erkennen, dass Du es Dir gerade _nicht_ einfacher machst, wenn Du Joins (um die man bei einer vernünftigen Datenbankanwendung nicht herumkommt) meidest, wie der Teufel das Weihwasser... 

LG


----------



## grünes-huhn (14. Juli 2009)

kuddeldaddeldu hat gesagt.:


> Hi,
> 
> 
> 
> ...




muss es dann in zwei Tabellen den gleichen Spaltennamen geben? Eben die, die miteinander verbunden sind. Das sind sie bei mir nämlich nicht.


----------



## Steiner_B (14. Juli 2009)

Bei einem NATURAL JOIN müssen sie gleich heißen. Das Äquivalent des NATURAL JOINS mit unterschiedlichen Namen ist ein INNER JOIN bei dem du im ON dann die Spalten angibst


----------



## kuddeldaddeldu (14. Juli 2009)

Hi,



grünes-huhn hat gesagt.:


> muss es dann in zwei Tabellen den gleichen Spaltennamen geben? Eben die, die miteinander verbunden sind. Das sind sie bei mir nämlich nicht.



Du hast innerhalb von 2 Minuten beide Artikel gelesen? :suspekt:

LG


----------



## grünes-huhn (14. Juli 2009)

kuddeldaddeldu hat gesagt.:


> Hi,
> 
> 
> 
> ...





natürlich nicht....
aber jetzt mal kurz für mich  :

Ich muss also die Abfrage die schon funktioniert nochmal in einer Select-Abfrage mit Join rein tun?
So in etwa:

SELECT * FROM a.order, (_Abfrage die schon funkt--select count...._) WHERE order_id=a.id

in der Art, oder bin ich total daneben? Ich weiß schon, ich sollte mich zuerst vernünftig einlesen, aber ich bin so verdammt ungeduldig


----------



## Steiner_B (14. Juli 2009)

Wo ist der Join? Was ist a.order?

Join grundsätzlich

```
SELECT * FROM a INNER JOIN b ON a.x = b.y
```
wobei a, b Tabellen; x,y die Attribute die in den Tabellen gleich sind


Und du hast glaub ich auch immer noch nicht erwähnt welches Datenbanksystem dahinter steckt.


----------



## grünes-huhn (14. Juli 2009)

Steiner_B hat gesagt.:


> Wo ist der Join? Was ist a.order?
> 
> Join grundsätzlich
> 
> ...



order ist die Tabelle wo das Datum der Bestellungen mitgespeichert wird. 

Ist eine Mysql Datenbank von dem Provider


----------



## grünes-huhn (14. Juli 2009)

aber vom Prinzip her ist es so, oder:

SELECT * FROM order INNER JOIN (_select count abfrage, die funkt_) ON order.id=prod.order_id


Wie muss ich die Select count abfrage verändern, damit es funktioniert?


----------



## kuddeldaddeldu (14. Juli 2009)

Hi,



grünes-huhn hat gesagt.:


> Ich muss also die Abfrage die schon funktioniert nochmal in einer Select-Abfrage mit Join rein tun?



Nein, natürlich nicht. Es soll ja einfacher werden, nicht noch komplizierter...



grünes-huhn hat gesagt.:


> Ich weiß schon, ich sollte mich zuerst vernünftig einlesen, aber ich bin so verdammt ungeduldig



Das ist mir wurscht. Ich mag nämlich Gequengel nach einer Fertiglösung nicht, wo ich Dir doch zwei wirklich gute Artikel gepostet habe. Also übe Dich in Geduld und mach Dir jetzt mal die Mühe, Dich da einzulesen. Schließlich hast _Du_ doch ein Problem, das gelöst werden muss.

LG


----------



## grünes-huhn (14. Juli 2009)

kuddeldaddeldu hat gesagt.:


> Hi,
> 
> 
> 
> ...




Ich hab schon, dass ich mir die zuletzt Bestellten auslese, jedoch ist mir echt nicht klar, wie ich diese count Geschichte in die Abfrage bekomm


----------



## kuddeldaddeldu (14. Juli 2009)

Hi,



grünes-huhn hat gesagt.:


> Ich hab schon, dass ich mir die zuletzt Bestellten auslese, jedoch ist mir echt nicht klar, wie ich diese count Geschichte in die Abfrage bekomm



zeige die Abfrage und das Ergebnis hier.

LG


----------



## grünes-huhn (14. Juli 2009)

kuddeldaddeldu hat gesagt.:


> Hi,
> 
> 
> 
> ...




SELECT * FROM order INNER JOIN orderedprod ON orderedprod.oprod_orderid=order.ord_id ORDER by order.ord_date DESC

Ergebnis ist, dass alle bestellten Produkte nach datum gereiht angezeigt werden.

Nun brauch ich irgendwie da drin eben mein count.


----------



## kuddeldaddeldu (14. Juli 2009)

Hi,

bitte gewöhne Dir doch mal an, SQL-Code strukturiert in Code- oder SQL-Tags zu posten.



grünes-huhn hat gesagt.:


> Ergebnis ist, dass alle bestellten Produkte nach datum gereiht angezeigt werden.
> 
> Nun brauch ich irgendwie da drin eben mein count.



Gruppiere die Abfrage halt nach dem Produktnamen und sortiere das nach count(irgendwas). Du willst doch gar nicht wirklich nach Datum sortieren. Schränke per WHERE-Klausel auf Deinen gewünschten Zeitraum ein.

Übrigens: wenn es eine Tabelle "order" gibt, in der die Bestellungen gespeichert werden, wozu soll die Tabelle "orderedprod" dann eigentlich gut sein? Speicherst Du da etwa Daten doppelt und zehnfach, um ja keinen Join machen zu müssen? Mit sowas handelt man sich mehr Probleme ein, als man löst.
Du brauchst doch eigentlich nur eine Tabelle "produkte", wo jedes Produkt *einmal* mit seinen Daten und einer eindeutigen ID drinsteht. In der Tabelle "order" hast Du dann ein Feld produkt_id, in dem genau diese ID drinsteht.

Befrage mal Google nach "Normalisierung"...

LG


----------

