Abfrageproblem bei einer nicht sauber normalisierten Tabelle
Reinhard
- datenbank
Hallo Forum,
ich hoffe, Ihr könnt mir helfen.
Ich habe eine Tabelle, die nicht sauber normalisiert ist. Im Großen und Ganzen istr das für die Programmbenutzung kein Problem.
Lediglich bei einer internen Statistik, die ich anfertige, bereitet es mir gerade Kopfzebrechen.
Hier die Strucktur:
ID|User|Punkte
Unter User können (das ist das Problem) mehrer User, durch Kommata getrennt enthalten sein. Die bekommen dann auch alle die in dieser Zeile vergebenen Punkte gutgeschrieben.
Nun möchte ich die gesamten in dieser Tabelle vergebenen Punkte errechnen.
Beispiel:
1|Hinz,Kunz|10
2|Fritz|20
3|Pit,Mike,Lutz|15
Die Query soll als Ergebniss 85 ergeben.
Wie werde ich dieser unnormalisierter Spalte in der benötigten Query Herr.
Ich habe schon mächtigst gesucht, bin bereits auf CASE und auf REGEXP '[[:<:]]...[[:>:]]' gestoßen, aber bekomme die Query auch nach Stunden suchen und versuchen nicht hin.
Bitte also daher hier um Hilfe,
Danke, Reinhard
Ein Hauch einer Möglichkeit hättest du nur wenn du eine weitere Tabelle hast mit allen Usernamen (*nicht* Kommagetrennt) und wenn dein verwendetes DBMS MySQL lautet.
Ein Hauch einer Möglichkeit hättest du nur wenn du eine weitere Tabelle hast mit allen Usernamen (*nicht* Kommagetrennt) und wenn dein verwendetes DBMS MySQL lautet.
Hi,
das ist zwar so, aber ich bezweifel, dass Du mit Deiner Behauptung Recht hast.
Warten wir mal ab, was die Profis zu dem Thema sagen.
Dennoch danke für die erste Antwort
Reinhard
tab1 ist deine o.g. Punktetabelle, tab2 die Tabelle der user
SELECT GROUP_CONCAT(user), punkte
FROM (
SELECT tab2.user, SUM(tab1.punkte) punkte
FROM tab1
JOIN tab2
ON FIND_IN_SET(tab2.user, tab1.user) > 0
GROUP BY tab2.user
) tab
GROUP BY punkte
moin,
die beiden gruppierungen kannst du dir sparen, laut seiner ergebnisliste will er eh alle punkte zusammen addiert haben und nicht aufgeteilt in den jeweiligen gruppen.
des weiteren läufst du damit gefahr, dass du falsche ergebnisse bekommst, weil namen als teilmenge in anderen namen enhalten sein können.
Ilja
die beiden gruppierungen kannst du dir sparen, laut seiner ergebnisliste will er eh alle punkte zusammen addiert haben und nicht aufgeteilt in den jeweiligen gruppen.
Ich sehe es anders, er will gruppiert nach Punkten, deiner M.n. müsste die Tabelle bspw. so aussehen
'Kunz,Hinz', 3
'Kunz,Hinz', 10
'Kunz,Hinz', 2
also die Gruppierungen in der Tabelle jedesmal exakt so wie im Ergebnis, das glaube ich aber eher weniger
des weiteren läufst du damit gefahr, dass du falsche ergebnisse bekommst, weil namen als teilmenge in anderen namen enhalten sein können.
Dafür ist FIND_IN_SET() zuständig
Hi,
Ich sehe es anders, er will gruppiert nach Punkten,
Ich möchte nur die gesamten vergebenen Punkte ermitteln.
Es geht also nur um 1 Zahl, die die Ergebnismenge liefern soll.
Gruß, Reinhard
Uups, das hatte ich ja komplett falsch verstanden, ich dachte dein Beispiel soll das Ergebnis darstellen.
So gesehen ist die Lösung von @Ilja natürlich richtig.
Sorry.
Uups, das hatte ich ja komplett falsch verstanden, ich dachte dein Beispiel soll das Ergebnis darstellen.
So gesehen ist die Lösung von @Ilja natürlich richtig.
Sorry.
Das ist ganz und gar kein Problem.
Vielen Dank aber trotzdem für Deine Mithilfe.
Grüße, Reinhard
tab1 ist deine o.g. Punktetabelle, tab2 die Tabelle der user
SELECT GROUP_CONCAT(user), punkte
FROM (
SELECT tab2.user, SUM(tab1.punkte) punkte
FROM tab1
JOIN tab2
ON FIND_IN_SET(tab2.user, tab1.user) > 0
GROUP BY tab2.user
) tab
GROUP BY punkte
>
Danke für den Code, aber so ganz ohne Erklärung verstehe ich ihn nicht :-(
Gruß, Reinhard
Danke für den Code, aber so ganz ohne Erklärung verstehe ich ihn nicht :-(
Sorry, aber der Code ist eh für ein anderes Problem, ich hatte dein erstes Posting falsch interpretiert.
moin,
ich hoffe, Ihr könnt mir helfen.
für ein frisches bier mache ich alles.....
Ich habe schon mächtigst gesucht, bin bereits auf CASE und auf REGEXP '[[:<:]]...[[:>:]]' gestoßen, aber bekomme die Query auch nach Stunden suchen und versuchen nicht hin.
das brauchst du alles nicht, es gibt dafür einen kleinen trick. ich kann dir die komplette abfrage liefern, aber ich verrate dir erst mal den trick und vielleicht kannst du ja den rest erstellen.
Du brauchst zwei funktionen, die du geschickt einsetzen musst, nämlich REPLACE UND LENGTH. wie genau die funktionen heißen, hängt ein wenig von deinem DBMS ab. aber schau dir mal folgendes an....
LENGTH(user) - LENGTH(REPLACE(user, ',', '')) + 1
Ilja
Hi Ilja,
für ein frisches bier mache ich alles.....
Liebend gerne...und ich trink auch selber eines mit *g*
Leider (oder in vielen anderen Fällen 'zum Glück') geht noch nicht alles virtuell :-(
Du brauchst zwei funktionen, die du geschickt einsetzen musst, nämlich REPLACE UND LENGTH. wie genau die funktionen heißen, hängt ein wenig von deinem DBMS ab. aber schau dir mal folgendes an....
Ich benutze mysql, Version 5
LENGTH(user) - LENGTH(REPLACE(user, ',', '')) + 1
Aah, jetzt fällt der Groschen, aber ich hab zugegebenerweise schon etwas dafür gebraucht ;-)
Du versuchst über die Differenz des Gesamttextes und des um die kommata verkürzten Gesamttextes an die Anzahl der Kommata zu kommen und hast folglich einen Namen mehr im Feld als Du kommatas hast, deshalb die Addition mit 1.
Ok, den Trick habe ich verstanden, aber die Umsetzung in mywsql-Sprache fällt mir trotzdem noch etwas schwer. Vielleicht aber auch, weil ich inzwischen nach etlichen Stunden Code schreiben "gefühlt" nur noch Spagetti im Kopf habe ;-)
Ich versuch mal:
SELECT
SUM(
(LENGTH(User) - LENGTH(REPLACE(User, ',', '' ))+1)
*Punkte)
FROM Tabelle
Ist das so richtig?
Gruß, Reinhard
Ich versuch mal:
SELECT
SUM(
(LENGTH(User) - LENGTH(REPLACE(User, ',', '' ))+1)
*Punkte)
FROM Tabelle
>
>
> Ist das so richtig?
>
Das konnte ich grade mal testen und es funktioniert tadellos.
Nun ist das Problem im Echtbetrieb doch noch etwas verzwickter.
Die Tabelle sieht nämlich so aus:
ID|tabellenverweis|spielID|status|punkte
1|A|1|neu|20
2|A|2|neu|30
3|B|2|neu|25
4|Z|22|alt|50
A bedeutet, dass ich die Spielerkombination in Tabelle spiel\_A finde, B bedeuted, dass ich sie in spiel:B finde.
spiel\_A:
spielID|spieler
1|Karl,Theo
2|Peter,Franz
3|Gerd,Tom
spiel\_B:
spielerID|spieler
1|Theo,Tom
2|Karl
Wie muß nun meine Abfrage lauten, damit ich als Ergebniss der neu verteilten Gesamtpunkte die Summe 125 erhalte?
125=2\*20+2\*30+1\*25
Gruß, Reinhard
moin,
auch wenn du eventuell nichts dafür kannst und vor allem es nicht ändern kannst, dein datendesign ist "unvorteilhaft", um es mal milde auszudrücken. das riecht ein wenig nach steinzeit.
Wie muß nun meine Abfrage lauten, damit ich als Ergebniss der neu verteilten Gesamtpunkte die Summe 125 erhalte?
am besten benutzt du dafür den UNION ALL Mengenoperator, sprich du hast zwei abfragen, eine für Tabelle A und eine für Tabelle B, die du dann zusammen fügst und die Summe darüber bildest.
SELECT SUM(z.Anzahl) Meine_Summe
FROM (SELECT (LENGTH(a.spieler) - LENGTH(REPLACE(a.spieler, ',', '')) +1) * t.Punkte Anzahl
FROM Tabelle t
INNER JOIN spiel_A a ON a.spielID = t.spielID
WHERE t.tabellenverweis = 'A'
UNION ALL
SELECT (LENGTH(b.spieler) - LENGTH(REPLACE(b.spieler, ',', '')) +1) * t.Punkte
FROM Tabelle t
INNER JOIN spiel_B b ON b.spielID = t.spielID
WHERE t.tabellenverweis = 'B'
) z
;
das würde ich jetzt als am übersichtlichsten ansehen. es gibt noch andere möglichkeiten mit einer unterabfrage oder aber joins. aber dann wird nicht ganz klar, was du damit machen willst.
Ilja