SQL [mySQL] Gruppierung mit Ausnahme

WulfmanGER

Commander
Registriert
Juli 2005
Beiträge
2.225
Hallo zusammen,

ich hab leider mehrere unglücklich erstellte Tabellen (nicht von mir -> ich importiere nur 1:1 von sqllite [calibre] zu mySQL) wo ich an einer Abfrage festhänge.

Mein "Basis"-Query sieht so aus:
Code:
select btl.book, b.title, bsl.series, s.edit
from books_tags_link btl, books b, books_series_link bsl
left join series s on s.id=bsl.series
where btl.book = b.id and btl.tag = 248 AND bsl.book=b.id

Ergebnis:
Code:
book(id)    |    title    |    series(id)    |    serienname
1            |    Buch 1    |    1            |    Tolle Romane über Tiefseefische
2            |    Buch 2    |    1            |    Tolle Romane über Tiefseefische
3            |    Buch 3    |    1            |    Tolle Romane über Tiefseefische
4            |    Buch 4    |    1            |    Tolle Romane über Tiefseefische
5            |    Buch 5    |    222            |    
6            |    Buch 6    |    249            |    
7            |    Buch 7    |    2            |    Storys aus dem Leben einer Kualquappe
8            |    Buch 8    |    2            |    Storys aus dem Leben einer Kualquappe
9            |    Buch 9    |    222            |    
10            |    Buch 10    |    222            |    
11            |    Buch 11    |    249            |
Ja die Abfrage greift auch auf nicht dargestellte Tabellen/Felder (*tags*) zu - das muss ich machen um von einem Schlagwort die Buchinformationen zu erhalten.

Das Ziel der Abfrage sollte aber eigentlich sein:
Ich bekomme alle Bücher angezeigt die mit dem Schlagwort/Tag "248" belegt sind. Exakt das macht die Abfrage. Aber das reicht mir so nicht. Bücher der gleichen Serie, sollen mit dem Seriennamen respräsentiert werden - Serienlose Bücher, mit ihrem Buchtitel.

ABER ... ich nutze das Quelltool für die Verwaltung und da ist es in meinem Fall von Vorteil JEDEM Buch eine Serie zuordnen. Serienlose-Bücher bekommen aber eine FAKE-Serie:

Im TOOL sieht das so aus:
Buch 5 -> Serie: Beletristik
Buch 1 -> Serie: Beletristik.Tolle Romane über Tiefseefische
Buch 6 -> Serie: Comic
Buch 123 -> Serie: Comic.Walt Disneys Best-off

Hier kann ich dann auf Comic klicken und bekomme alle Comics unter "Comic" und unter "Comic." angezeigt. Das will ich DORT auch so haben. In meinem mySQL-PHP-Version wird das leider zu einem logistischen Problem... wie ich gerade merke ...

Kurz: ich suche eine Abfrage die Quasi bei existierenden Seriennamen auf eben diesen Gruppiert, bei fehlenden Seriennamen DIESEN in Serienname reinschreibt (damit ich bei der php-Abfrage nicht ständig 2 spalten switchen muss)

Das WUNSCH-Ergebnis wäre also:
Code:
book(id)    |    title    |    series(id)    |    serienname
1            |    Buch 1    |    1            |    Tolle Romane über Tiefseefische
5            |    Buch 5    |    222            |    Buch 5
6            |    Buch 6    |    249            |    Buch 6
7            |    Buch 7    |    2            |    Storys aus dem Leben einer Kualquappe
9            |    Buch 9    |    222            |    Buch 9
10            |    Buch 10    |    222            |    Buch 10
11            |    Buch 11    |    249            |    Buch 11
222, 249 sind besagte Fake-Serien (gibt es mehrere von)

Ich hab es schon mit CONCAT versucht - aber das fügt halt title und serienname zusammen - gut wenn kein Serienname da ist, sonst schlecht :)
Code:
select btl.book, b.title, bsl.series, s.edit, CONCAT(b.title, s.edit) as bla
...
group by bla

COALESCE = "zweites" überschreibt "erstes"

Code:
select btl.book, b.title, bsl.series, s.edit, COALESCE(b.title, s.edit) btse
...
group by bla
CONCAT_WS hilft auch nicht.

Mit having nach dem Group bekomme ich nur entweder oder ANGEZEIGT. having series = 222 -> nur Bücher der Fakeserie 222 werden angezeigt. Mit != alles außer diese werden Gruppiert ...

Bin kurz davor die Gesamtmasse in eine Temporäre Tabelle zu schreiben und diese mittels php erstmal formen und dann auslesen. Aber das jetzt nicht gerade positiv für die Systemlast :)

Man könnte wohl mit IF, CASE in mySQL arbeiten - da blicke ich aber noch nicht so recht durch :( Wenn "Serienname" = LEER dann füge title in serienname ein :=) ... das ganze per Group by von duplikaten befreien - fertig. Aber wie?

Neben mySQL wird php zum Auslesen genutzt.

Danke schon mal :)
 
Hast du einen Series Datensatz mit der ID 222 und der Serienname ist NULL oder ''(leer) oder vergibst du nur eine ID die es in der Series-Tabelle nicht gibt?

CASE WHEN könnte in etwa so aussehen :

SQL:
CASE WHEN LEN(ISNULL(s.Edit,'')) = 0 THEN
    b.Title
ELSE
    s.Edit
END AS DisplayName

Was du mit der Gruppierung vorhast, kann ich mir nicht ganz vorstellen. Weiß nicht genau wie das bei mySQL ist aber bei MSSQL kannst du nur über alle selektierten Spalten gruppieren.
 
case when ist die funktion die du suchst, wobei ich jetzt auch nicht ganz kapiere was du gruppieren willst ^^
 
EDIT ... ich musste DIESEN Post komplett editieren (falls gerade wer noch was anderes gelesen hat) - war etwas verwirrend ...

Tabelle "series": hier steht eine ID drin und ein Name (in 2 Varianten - Plain und Edit - ich muss EDIT nutzen): series.id und series.edit

Tabelle "books_series_link" ist die Verknüpfungstabelle um von Bücher-IDs (books.id) auf Serien zu kommen. Hier hab ich die die Serien-ID drin (books_series_link.series), welches mit series.id matchbar ist und die series.book mit der ich ein Match in die Buch-Tabelle machen kann.

Ich muss prüfen ob die Serie = Fakeserie ist (also 222, 249 etc.) - ist das der Fall, kommt der Buchtitel in ein neues Feld, ist das nicht der Fall, kommt der Serientitel in das neue Feld

Code:
select *, CASE innerSel.series WHEN 222 THEN
    innerSel.title
ELSE
    innerSel.edit
END AS DisplayName FROM (
select btl.book, b.title, bsl.series, s.edit
from books_tags_link btl, books b, books_series_link bsl
left join series s on s.id=bsl.series
where btl.book = b.id and btl.tag = 248 AND bsl.book=b.id )  innerSel

damit komme ich der sache schon SEHR nahe ... oder anders gesagt: abgesehen von einer nicht weiterhelfenden fehlermeldung, klappt das schon... WHEN muss ich dann noch erweitern und am Ende group by DisplayName.

Code:
Notice in .\libraries\sql-parser\src\Utils\Query.php#427
[IMG]xxxxx[/IMG] Undefined property: SqlParser\Components\CaseExpression::$expr


EDIT 2:
Ich glaub ich hab es dann ... ohne Fehlermeldung ... der CASE-Block muss in ()

Code:
select *, (CASE innerSel.series
        WHEN 222 THEN innerSel.title
        WHEN 249 THEN innerSel.title
        ELSE innerSel.edit
    END) AS DisplayName
FROM (
    select btl.book, b.title, bsl.series, s.edit
        from books_tags_link btl, books b, books_series_link bsl
        left join series s on s.id=bsl.series
        where btl.book = b.id and btl.tag = 248 AND bsl.book=b.id
    )  innerSel
group by DisplayName 
ORDER BY `innerSel`.`series` ASC

WHEN fülle ich dann per php in einer Schleife. So kann ich die FAKE-Serien in einem Array pflegen ...


DANKE für den CASE-Ansatz!
 
Zuletzt bearbeitet:
Gewöhn dir bitte dringend nur eine der beiden Schreibweisen für einen Join an. Die Mischung ist grausam zum lesen.

Wenn ich mich nicht versehe, geht auch ein ISNULL auf den Wert der Left Join Tabelle mit dem Abfangwert als Titel. Damit wäre es dynamischer als mit deinen Konstanten.
 
Zuletzt bearbeitet:
ISNULL würde sicherlich auch gehen - aber ich hatte da noch einen Fehler in meiner Logik gefunden. Wenn s.edit leer ist und ich den Titel reinschreiben will, ist das zwar erstmal richtig - aber es gibt auch paar echt blöde Seriennamen die einfach nur ein "Fakt" zusammenfassen. s.edit ist somit nicht leer - aber nicht richtig. Ich muss also doch auf die serien ids gehen. In ein array in php schreibe ich diese ganzen IDs (~10 werden es wohl werden) und füge die dann in das "WHEN" ein. Zwischen "WHEN" und "THEN" ist aber vermutlich kein OR möglich. Das könnte dann etwas übersichtlicher werden. Aber letztendlich geht es auch mit ~10 WHEN-THENs. Natürlich bedeutet das auch das ich das array pflegen muss ... wobei hier vorwiegend ALT-LASTEN und Sonderfälle behandelt werden - da sollte nichts neues mehr dazukommen.

JA die Schreibweise ist gerade etwas ... Aber du meinst wohl nicht das groß/klein. Wie wäre hier die richtige Schreibweise? Ich rücke eigentlich auch alles was irgendwo "untergeordnet" ist, weiter ein (hier beim join nicht gemacht...)
 
Es gibt kein richtig oder falsch, wichtig ist die Lesbarkeit und Konsequenz. Ich schreib alle syntaktischen Begriffe wie SELECT bspw immer groß.
Es geht primär um die Komma Joins, schreib doch einfach INNER JOIN und rücke alles ein.

Zu deiner Frage: Im CASE WHEN geht ein OR als Bedingung, allerdings machst Du ja nur eine Aufzählung von Konstanten, also reicht wohl ein IN(...).
 

Ähnliche Themen

Zurück
Oben