MYSQL: GROUP_CONCAT() bekommt zu viele Ergebnisse

Fragen zu allen Themen rund ums Programmieren außerhalb von phpBB können hier gestellt werden - auch zu anderen Programmiersprachen oder Software wie Webservern und Editoren.
Antworten
Benutzeravatar
mgutt
Mitglied
Beiträge: 2999
Registriert: 31.08.2004 16:44
Wohnort: Hennef
Kontaktdaten:

MYSQL: GROUP_CONCAT() bekommt zu viele Ergebnisse

Beitrag von mgutt »

Ich versteh das einfach nicht:

Code: Alles auswählen

SELECT DISTINCT(u.uid), u.gender, u.username, p.hobby, pic.pid, pic.ext, GROUP_CONCAT( '<a href="./?p=groups&gid=', g.uid, '">', g.group_name, '</a>' ORDER BY g.group_name SEPARATOR ', ' ) group_name, c.message
FROM contacts c, users u
LEFT JOIN pics pic ON pic.uid = c.cid
LEFT JOIN (groups ug, users g) ON u.uid = ug.uid AND ug.gid = g.uid
LEFT JOIN profiles p ON p.user_id = u.uid
WHERE c.uid = 1
AND c.cid = u.uid
AND c.uagree = 1
GROUP BY u.uid
ORDER BY u.username ASC
Wie man sieht habe ich drei variable Tabellen (User können Bilder haben, User können Gruppen haben, User können Profile haben)

Die Tabelle "profiles" ist jetzt neu hinzugekommen. Ich musste aber vorher schon mit GROUP BY und DISTINCT dafür sorgen, dass User nicht doppelt ausgegeben werden.

Jetzt habe ich, aber wie so oft ein Problem mit GROUP_CONCAT().

GROUP_CONCAT() geht eine variable Verbindung mit profiles ein und dadurch werden bei Usern, die mehr als ein Profil angelegt haben die Gruppen mehrfach angezeigt (User = drei Profile = dreifache Darstellung der Gruppen).

Das sieht dann so aus.
Username | Gruppe1, Gruppe1, Gruppe1, Gruppe2, Gruppe2, Gruppe2

Richtig wäre aber:
Username | Gruppe1, Gruppe2

Da bei den Bildern und den Gruppen derzeit nicht viele dabei sind, die mehrere Bezüge haben, gehe ich davon aus, dass der Fehler vorher einfach noch nicht aufgefallen ist. Doch jetzt legen die User ihre Profile in mehrere Sprachen an und schon kommt es zu fehlerhaften Ergebnissen.

Auf den meisten Seiten kann ich das unterbinden, in dem ich nur die Profile anzeigen lassen, deren Sprache sich mit der Besuchersprache deckt. Aber bei der Suche möchte ich alle Profile gleichzeitig ansprechen können.

Ich würde mir ja ein DISTINCT oder ein GROUP BY in dem GROUP_CONCAT() wünschen, aber das geht irgendwie nicht. Wie könnte ich das lösen?
meine Foren: http://www.maxrev.de/communities.htm
Ich kaufe Dein Forum! Angebote bitte an marc at gutt punkt it
PhilippK
Vorstand
Vorstand
Beiträge: 14662
Registriert: 13.08.2002 14:10
Wohnort: Stuttgart
Kontaktdaten:

Beitrag von PhilippK »

Hast du es mal testweise nur mit

Code: Alles auswählen

GROUP_CONCAT(g.group_name ORDER BY g.group_name SEPARATOR ', ' )
probiert? Ich weiß nicht, ob der von dir verwendete Syntax so ganz passt - der ist etwas mau beschrieben.

Gruß, Philipp
Kein Support per PN!
Der Sozialstaat ist [...] eine zivilisatorische Errungenschaft, auf die wir stolz sein können. Aber der Sozialstaat heutiger Prägung hat sich übernommen. Das ist bitter, aber wahr. (Horst Köhler)
Meine Mods
Benutzeravatar
Pyramide
Ehrenadmin
Beiträge: 12734
Registriert: 19.04.2001 02:00
Wohnort: Meschede

Beitrag von Pyramide »

Naja wenn ein User zwei Gruppen und drei Profile hat, dann macht das bei mir 1*2*3 = 6, was ja auch mit dem Ergebnis übereinstimmt. Wenn du nur die Gruppen oder nur die Profile brauchst, kannst du ja die jeweils andere Tabelle z.B. mit einem Subquery abfragen.
KB:knigge
Benutzeravatar
mgutt
Mitglied
Beiträge: 2999
Registriert: 31.08.2004 16:44
Wohnort: Hennef
Kontaktdaten:

Beitrag von mgutt »

PhilippK hat geschrieben:Hast du es mal testweise nur mit

Code: Alles auswählen

GROUP_CONCAT(g.group_name ORDER BY g.group_name SEPARATOR ', ' )
probiert? Ich weiß nicht, ob der von dir verwendete Syntax so ganz passt - der ist etwas mau beschrieben.

Gruß, Philipp
Kommt das gleiche bei raus.
Pyramide hat geschrieben:Naja wenn ein User zwei Gruppen und drei Profile hat, dann macht das bei mir 1*2*3 = 6, was ja auch mit dem Ergebnis übereinstimmt. Wenn du nur die Gruppen oder nur die Profile brauchst, kannst du ja die jeweils andere Tabelle z.B. mit einem Subquery abfragen.
D.h. um z.B. das "p.hobby" aus dem Profil zu ermitteln soll ich so tun?

Code: Alles auswählen

WHERE p.hobby (SELECT FROM profiles p WHERE p.user_id = u.uid)
Damit habe glaube ich ein Problem. Denn wenn ich eine Volltextsuche mache, müsste ich mehrere Volltextsuchen im Subquery unterbringen. Das dürfte doch ziemlich langsam sein oder?

Gruß
meine Foren: http://www.maxrev.de/communities.htm
Ich kaufe Dein Forum! Angebote bitte an marc at gutt punkt it
PhilippK
Vorstand
Vorstand
Beiträge: 14662
Registriert: 13.08.2002 14:10
Wohnort: Stuttgart
Kontaktdaten:

Beitrag von PhilippK »

Kommt bei

Code: Alles auswählen

SELECT DISTINCT u.uid, u.gender, u.username, p.hobby, pic.pid, pic.ext, g.group_name, c.message 
FROM contacts c, users u 
LEFT JOIN pics pic ON pic.uid = c.cid 
LEFT JOIN (groups ug, users g) ON u.uid = ug.uid AND ug.gid = g.uid 
LEFT JOIN profiles p ON p.user_id = u.uid 
WHERE c.uid = 1 
AND c.cid = u.uid 
AND c.uagree = 1 
GROUP BY u.uid 
ORDER BY u.username ASC
das richtige raus oder ist da auch noch was doppelt?
Wenn nein würde ich das in 'ne zweite Abfrage einbetten, die dann die Verknüpfung vornimmt.

Gruß, Philipp
Kein Support per PN!
Der Sozialstaat ist [...] eine zivilisatorische Errungenschaft, auf die wir stolz sein können. Aber der Sozialstaat heutiger Prägung hat sich übernommen. Das ist bitter, aber wahr. (Horst Köhler)
Meine Mods
Benutzeravatar
mgutt
Mitglied
Beiträge: 2999
Registriert: 31.08.2004 16:44
Wohnort: Hennef
Kontaktdaten:

Beitrag von mgutt »

Nein, dann ist nichts doppelt, aber ich frage mich gerade warum g.group_name leer ist. :-?

Du meinst eine zweite Abfrage pro Zeile? Das finde ich etwas heftig bei 20-30 Zeilen pro Seite.

EDIT:

Ich habe mal geschaut. Wenn ich DISTINCT UND GROUP BY weglasse, dann sehe ich das Problem denke ich.

Diese Zeile sorgt dafür, dass jedes Mitglied exakt 12 Mal erscheint:

Code: Alles auswählen

LEFT JOIN (groups ug, users g) ON u.uid = ug.uid AND ug.gid = g.uid
Hier noch mal die komplette Abfrage, die ich beim Testen genommen habe:

Code: Alles auswählen

SELECT u.uid, u.gender, u.username, g.group_name, c.message
FROM contacts c, users u
LEFT JOIN (groups ug, users g) ON ug.uid = u.uid AND ug.gid = g.uid
WHERE c.uid = 1
AND c.cid = u.uid
AND c.uagree = 1
ORDER BY u.username ASC
Das dürfte eigentlich nicht sein, da jedes Mitglied nur so oft erscheinen müsste, wie es in Gruppen ist.

Wenn ich übrigens "(groups ug, users g)" umdrehe in "(users g, groups ug)", dann erscheint jedes Mitglied 41 Mal. Und wenn ich bei dem LEFT JOIN "AND g.group_name <> NULL" hinzufüge, dann verschwinden die Zeilen, wo der Name vorhanden ist und nur die NULL Zeilen bleiben. Auch sehr komisch.

EDIT2:
Einfacher als ich dachte, konnte ich die Anzahl der Reihen reduzieren:

Code: Alles auswählen

SELECT u.uid, u.gender, u.username, p.hobby, pic.pid, pic.ext, g.group_name, c.message
FROM contacts c, users u
LEFT JOIN pics pic ON pic.uid = c.cid
LEFT JOIN groups ug ON ug.uid = u.uid
LEFT JOIN users g ON ug.gid = g.uid
LEFT JOIN profiles p ON p.user_id = u.uid
AND p.lang_id =1
WHERE c.uid =1
AND c.cid = u.uid
AND c.uagree =1
ORDER BY u.username ASC
Hier kommt das Ergebnis ohne doppelte Namen raus. Auch wenn ich GROUP_CONCAT() nutze.

Aber das liegt nur daran, dass ich profiles mit "lang_id" auf ein Ergebnis reduziere.

Das ist aber falsch. Denn wenn ich z.B. einen englischsprachigen User anschaue und meine Sprache ist Deutsch, dann werde ich keine Hobbys sehen. Daher müsste es eigentlich so sein "WENN lang_id = meine Sprache vorhanden, dann nehmen, SONST lang_id = Hauptsprache des Profilbesitzers"

Das funktioniert dann aber nicht mehr bei der Suche. Bei der Suche möchte ich in allen Profilen gleichzeitig suchen. Das funktioniert super, aber nur, wenn er in einem Profil was findet. Wenn er ein Wort in zwei Profilen eines Users findet, dann erscheinen die Vereine wieder doppelt. z.B. das Wort "Skaten" könnte sowohl im deutschen als auch englischen Profil des Nutzers vorkommen und schon ist es passiert.

D.h. ich brauche eine Sortierungs- und Limitierungsmöglichkeit.

Perfekt wäre das:
1.) Findet er nichts, dann die Sprache des Anwenders ausgeben (lang_id = 1 z.B. für Deutsch)
2.) Findet er in beiden Ergebnissen etwas, dann soll das angezeigt werden, was wahrscheinlicher ist (die Reihenfolge müsste bereits durch die Volltextsuche vorgenommen sein)

Punkt 2 funktioniert ja im Endeffekt schon. Denn bei der Ausgabe erscheinen die Hobbys, die sich mit der Sucheingabe eher decken. Aber ich sehe daran, dass die Gruppen der Person doppelt angezeigt werden, dass das Suchwort auch in seinem anderssprachigen Profil vorhanden gewesen sein muss. Also müsste ich für Punkt 2 ein LIMIT haben.

Punkt 1 dagegen wäre eine Art IF Geschichte in meinen Augen, womit ich mich ehrlich gesagt gar nicht auskenne :-?

EDIT3:
Hier die Abfrage bei der Suche. Derzeit erhalte ich dabei 2 Ergebnisse, weil User ID 8 zwei Profile angelegt hat:

Code: Alles auswählen

SELECT u.uid, u.gender, u.username, u.group_name AS groupname, p.hobby, u.dob, g.group_name
FROM users u
LEFT JOIN groups ug ON ug.uid = u.uid
LEFT JOIN users g ON ug.gid = g.uid
LEFT JOIN profiles p ON p.user_id = u.uid
WHERE u.active = 1
AND u.uid = 8
EDIT4:
Ich habe mal eine IF Bedingung versucht:

Code: Alles auswählen

SELECT u.uid, u.gender, u.username, u.group_name AS groupname, p.hobby, u.dob, IF( p.lang_id =1, g.group_name, '') 
FROM users u
LEFT JOIN groups ug ON ug.uid = u.uid
LEFT JOIN users g ON ug.gid = g.uid
LEFT JOIN profiles p ON p.user_id = u.uid
WHERE u.active =1
AND u.uid =8
Dann erhalte ich zwar immer noch zwei Zeilen, aber die zweite Gruppenname bleibt leer.

Wenn ich das erweitere auf:

Code: Alles auswählen

SELECT u.uid, u.gender, u.username, u.group_name AS groupname, p.hobby, u.dob, IF( p.lang_id =1, g.group_name, IF( p.lang_id =2, g.group_name, '' ) ) 
Dann erhalte ich wieder beide Ergebnisse, da beim ersten Durchlauf die erste Bedingung greift und beim zweiten die erste verfehlt und dann die zweite greift. So habe ich dann wieder zwei Ergebnisse mit zwei Gruppennamen.

Ich müsste es irgendwie so machen, dass wenn die erste lang_id gefunden wurde, dass er dann aufhört zu suchen.

EDIT5:

Selbst wenn ich p.user_id in GROUP BY nehme erscheinen die Gruppen doppelt. Wenn ich GROUP_CONCAT() aber weglasse erhalte ich nur eine Zeile:

Code: Alles auswählen

SELECT DISTINCT(u.uid), u.gender, u.username, u.group_name AS groupname, p.hobby, u.dob, GROUP_CONCAT( '<a href="./?p=groups&gid=', g.uid, '">', g.group_name, '</a>' ORDER BY g.group_name SEPARATOR ', ' ) group_name
FROM users u
LEFT JOIN groups ug ON ug.uid = u.uid
LEFT JOIN users g ON ug.gid = g.uid
LEFT JOIN profiles p ON p.user_id = u.uid
WHERE u.active = 1
AND u.uid = 8
GROUP BY p.user_id, u.uid
EDIT6:
So damit erhalte ich nun alle Mitglieder sortiert nach ihrem Anmeldedatum und dank DISTINCT im GROUP_CONCAT() auch nur einmal die Vereine:

Code: Alles auswählen

SELECT u.uid, u.gender, u.username, u.group_name AS groupname, p.hobby, u.dob, pic.pid, pic.ext, GROUP_CONCAT(DISTINCT '', g.group_name, '' ORDER BY g.group_name SEPARATOR ', ' ) group_name
FROM users u
LEFT JOIN pics pic ON pic.uid = u.uid
LEFT JOIN groups ug ON ug.uid = u.uid
LEFT JOIN users g ON ug.gid = g.uid
LEFT JOIN profiles p ON p.user_id = u.uid
WHERE u.active = 1
AND u.uid <> 1
GROUP BY u.uid
ORDER BY u.regtime DESC
LIMIT 200
Jetzt brauche ich nur noch etwas, mit dem ich die Reihenfolge aus "LEFT JOIN profiles p ON p.user_id = u.uid" so beeinflussen kann, dass nur lang_id = X (meine Sprache) und dann lang_id = Y (Sprache des Users) ausgelesen wird, wobei meine Sprache als erstes ausgegeben werden soll.

Da ich ja nur eine Zeile Dank "GROUP BY u.uid" erhalte, so würde ich immer erst meine Sprache sehen, sofern der User ein Profil in meiner Sprache angelegt hat.

Wenn ich folgendes mache beeinflusst das leider nicht die Reihenfolge im Ergebnis:

Code: Alles auswählen

SELECT p.lang_id, u.uid, u.gender, u.username, u.group_name AS groupname, p.hobby, u.dob, g.group_name
FROM users u
LEFT JOIN groups ug ON ug.uid = u.uid
LEFT JOIN users g ON ug.gid = g.uid
LEFT JOIN profiles p ON p.user_id = u.uid AND (p.lang_id = 2 OR p.lang_id = 1)
WHERE u.active =1
AND u.uid <>1
AND u.uid = 8
ORDER BY u.regtime DESC 
LIMIT 200
Die erste Zeile ist lang_id 1 und die zweite Zeile lang_id 2. Denkt dran, ein ORDER BY hilft hier nicht, da ich nicht weiß, welche lang_ids da stehen könnten. Da könnte jetzt auch lang_id 8 (meine) und lang_id 4 (seine) gesucht sein.

Beispiel wie es in der Datenbank gespeichert wurde ohne Sortierung:
1
2
3
4
5

Ich will zuerst 5 und dann 3, Rest ist mir egal:
5
3

Wie könnte man etwas so auslesen?

Gruß

EDIT7:
Ok, super ich denke ich habs. Hier gefunden:
http://mysql-faq.sourceforge.net/anwendung2.html

Code: Alles auswählen

SELECT p.lang_id, u.uid, u.gender, u.username, u.group_name AS groupname, p.hobby, u.dob, g.group_name
FROM users u
LEFT JOIN groups ug ON ug.uid = u.uid
LEFT JOIN users g ON ug.gid = g.uid
LEFT JOIN profiles p ON p.user_id = u.uid
WHERE u.active =1
AND u.uid <>1
AND u.uid =8
ORDER BY u.regtime DESC , p.lang_id <> 2, p.lang_id <> 1
LIMIT 200
War ORDER BY doch der richtige Ansatz :P
meine Foren: http://www.maxrev.de/communities.htm
Ich kaufe Dein Forum! Angebote bitte an marc at gutt punkt it
PhilippK
Vorstand
Vorstand
Beiträge: 14662
Registriert: 13.08.2002 14:10
Wohnort: Stuttgart
Kontaktdaten:

Beitrag von PhilippK »

Geht doch :-)

Gruß, Philipp
Kein Support per PN!
Der Sozialstaat ist [...] eine zivilisatorische Errungenschaft, auf die wir stolz sein können. Aber der Sozialstaat heutiger Prägung hat sich übernommen. Das ist bitter, aber wahr. (Horst Köhler)
Meine Mods
Benutzeravatar
mgutt
Mitglied
Beiträge: 2999
Registriert: 31.08.2004 16:44
Wohnort: Hennef
Kontaktdaten:

Beitrag von mgutt »

Jo, aber ich sag Dir nicht, dass ich erst um 3 Uhr fertig geworden bin ;)

Was ist jetzt noch nicht getestet habe ist, wie viele Profile er lädt, wenn z.b. in einer Volltextsuche das Suchwort in keinem Profil vorkommt.

Das wäre dann der einzige Fall, wo ich keine Lösung hätte. Denn dann bräuchte ich tatsächlich ein IF. Von wegen "IF nichts gefunden und viele Profile, dann wieder ORDER BY meine Sprache, seine Sprache" :roll:
meine Foren: http://www.maxrev.de/communities.htm
Ich kaufe Dein Forum! Angebote bitte an marc at gutt punkt it
Antworten

Zurück zu „Coding & Technik“