igather
Goto Top

3SQL Abfragen zu einer zusammenfassen? (kompl. problem - JOIN oder CASE?)

Hallo

Bin neu hier da ich hilfe für ein kompliziertes problem suche! Ich habe Grundkenntnisse in den gängigsten Webentwicklungssprachen und nun bin ich dabei, mein erstes Modul für Joomla zu schreiben. Folgendes Problem. Ich habe eine Tabelle mit allen Kommentaren meiner Community-Seite. Diese enthält kommentare jeweils zu Artikeln, Bildern und Videos.

Die (wesentliche) Struktur sieht so aus:

Kommentare: jos_jcomments

id - object_id - object_group - username - date usw.

Die object_id enthält jeweils den Schlüssel zu den anderen Komponenten, die object_group bezeichnet die andere komponente


Die anderen Tabellen haben folgende struktur

Bilder: jos_joomgallery

id - catid - imgtitle - imgdate - published


Artikel: jos_content

id - title - state - sectionid - catid


Videos: jos_hwdvidsvideos

id - title


Mein Modul soll die neuesten Kommentare seit dem letzten Login des Users ausgeben. Hier scheitert es bei mir, das ganze in eine Abfrage zusammenzufassen.

Wenn das Kommentar für ein Video verfasst ist, steht in der object_group die entsprechende komponente, in dem fall brauche ich dann aus der Videotabelle den Titel, bei einem Bilderkommentar brauche ich aus der Bildertabelle den Titel, und bei einem Artikelkommentar die category-id und den title (section-id um zu prüfen ob der artikel aus dem community-bereich stammt)

Diese 3 Abfragen machen genau das was ich möchte, habe sie auch erfolgreich über phpmyadmin getestet:

SELECT j.username,j.object_id,j.date,c.catid,c.titleFROM jos_jcomments AS jINNER JOIN jos_content AS c ON c.id = j.object_idWHERE j.date < '2010-07-21 00:09:37'AND j.date > '2010-07-20 18:09:37'AND j.published = 1 AND j.object_group = 'com_content'ORDER BY j.date DESCSELECT j.username, j.object_id, j.date, g.imgtitleFROM jos_jcomments AS jINNER JOIN jos_joomgallery AS g ON g.id = j.object_idWHERE j.date < '2010-07-21 00:09:37'AND j.date > '2010-07-20 18:09:37'AND j.published =1AND j.object_group = 'com_joomgallery'ORDER BY j.date DESCSELECT j.username, j.object_id, j.date, v.titleFROM jos_jcomments AS jINNER JOIN jos_hwdvidsvideos AS v ON v.id = j.object_idWHERE j.date < '2010-07-21 00:09:37'AND j.date > '2010-07-20 18:09:37'AND j.published =1AND j.object_group = 'com_hwdvideoshare_v'ORDER BY j.date DESC


Aber es ist doch möglich, dies alles in einer Abfrage zu machen, da ich den rückgabewert aus der funktion wieder zurückgeben muss, und der return kann ja nur ein array haben (die 3 ergebnisse in ein array zu packen ist doch unnötig)

So meine bisheringe Versuche sehen so aus:

$query = "SELECT j.username,j.date,j.object_group,j.object_id,cont.catid,cont.title AS artitle,gal.imgtitle AS imgtitle,vids.title AS vidtitle" . "\n FROM #__jcomments AS j" . "\n INNER JOIN #__content AS cont ON cont.id = j.object_id" . "\n INNER JOIN #__joomgallery AS gal ON gal.id = j.object_id" . "\n INNER JOIN #__hwdvidsvideos AS vids ON vids.id = j.object_id" . "\n WHERE j.date < '$now'" . "\n AND j.date > '$userlastvisit'" . "\n AND j.published = 1 " . "\n AND (cont.sectionid = '3' OR j.object_group = 'com_hwdvideoshare_v' OR j.object_group = 'com_joomgallery')" . "\n ORDER BY j.date DESC" ; $db->setQuery( $query );

Beim ausführen erhalte ich keine Fehlermeldung, aber das Query registriert nur die Kommentare zu der Komponenten joomgallery?

Mein anderer Lösungsansatz sieht so aus (hier ohne php da ich das direkt in phpmyadmin teste)

SELECT j.username,j.object_id,j.dateCASE j.object_groupWHEN 'com_content' THEN SELECT c.catid, c.title FROM c WHERE (c.id = j.object_id AND c.sectionid = '3')WHEN 'com_joomgallery' THEN SELECT g.imgtitle FROM g WHERE g.id = j.object_idWHEN 'com_hwdvideoshare_v' THEN SELECT v.title FROM v WHERE v.id = j.object_idEND CASEFROM jos_jcomments AS j, jos_content AS c, jos_joomgallery AS g, jos_hwdvidsvideos AS vWHERE j.date < '2010-07-21 00:09:37'AND j.date > '2010-07-20 18:09:37'AND j.published = 1ORDER BY j.date DESC

Hierbei erhalte ich die Fehlermeldung:

#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'CASE

Ich kann aber beim besten willen nicht rausfinden wo der syntaxfehler sein soll, habe schon viele verschiedene möglichkeiten probiert mit klammern usw.


Das Problem ist doch, dass das Array nachher verschiedene felder hat, die leer bleiben, da nur einige der kommentare eine cat_id haben. Ebenfalls ist ein problem, dass die object_id des kommentars die gleichen sein können in der content und joomgallery, so hatte ich bei einem test zwar dein eintrag, aber der link bei der ausgabe verwies hatte den falschen titel (es sollte ein bild sein aber es war der titel eines artikels)

(Das Modul soll am ende eine Liste ausgeben, wo der Verfasser des Kommentars steht, in welchem Bereich (also artikel oder bilder usw..), Link zu dem Artikel/Bild/Video und das Datum der Erstellung)

So also vielen Dank für alle die sich schonmal die mühe gemacht haben, das alles zu lesen, ich hoffe ich habe das Problem ausreichend beschrieben, bin wirklich dankbar über jede hilfe, da ich kein SQL-Profi bin! Falls noch irgendwelche Angaben benötigt werden, reiche ich es sofort nach! Ahso ich teste es auf einer aktuellen Xampp auf meinem Vista...

Danke!!!!

Content-Key: 147368

Url: https://administrator.de/contentid/147368

Printed on: April 20, 2024 at 00:04 o'clock

Member: Biber
Biber Jul 21, 2010 at 19:42:06 (UTC)
Goto Top
Moin Lgather,

willkommen im Forum.

Lass mal kurz die handwerklichen SQL-Befehle einen Moment beiseite.
Ist ja im ersten Schritt erstmal wichtiger die logische Struktur der Gesamtabfrage vor mindestens einem der geistigen Augen zu haben.

Wenn du einen oder anderthalb Schritte zurücktrittst und draufschaust, dann

  • ist sicherlich die Aussage "Die drei Einzelabfragen machen was sie sollen" richtig
SELECT j.username,j.object_id,j.date,c.catid,c.title
FROM jos_jcomments AS j
INNER JOIN jos_content AS c ON c.id = j.object_id
WHERE j.date < '2010-07-21 00:09:37'  
AND j.date > '2010-07-20 18:09:37'  
AND j.published = 1
AND j.object_group = 'com_content'  
ORDER BY j.date DESC

SELECT j.username, j.object_id, j.date, g.imgtitle
FROM jos_jcomments AS j
INNER JOIN jos_joomgallery AS g ON g.id = j.object_id
WHERE j.date < '2010-07-21 00:09:37'  
AND j.date > '2010-07-20 18:09:37'  
AND j.published =1
AND j.object_group = 'com_joomgallery'  
ORDER BY j.date DESC

SELECT j.username, j.object_id, j.date, v.title
FROM jos_jcomments AS j
INNER JOIN jos_hwdvidsvideos AS v ON v.id = j.object_id
WHERE j.date < '2010-07-21 00:09:37'  
AND j.date > '2010-07-20 18:09:37'  
AND j.published =1
AND j.object_group = 'com_hwdvideoshare_v'  
ORDER BY j.date DESC

Was du danach versuchst, ist aber nicht "ich will das Beste, das Ergebnis aus Abfrage1 UND Abfrage2 UND Abfrage3 zusammenschaufeln",
sondern "Ich will EINE Abfrage, die mit den Bedingungen von Abfrage1 UND Abfrage2 UND Abfrage3 funktioniert."

Das ist inhaltlich etwas ganz anderes - so auch das Ergebnis.

(Wenn du nächstes Wochenende alle Frauen aus deiner Strasse einlädst, die ledig oder rothaarig oder unter 1,85m oder unter 20 sind, dann könnte es voll werden.
Wenn du nächstes Wochenende alle Frauen einlädst, die ledig und rothaarig und unter 1,85m und unter 20 sind, wird es ein anderes Ergebnis)

Ich würde in diesem Fall (kann ja sein, dass dein user Comments SOWOHL bei videos WIE AUCH bei Bilder WIE bei whatever stehen hat... oder nur eins von dreien..) zumindest als Proof-of-concept ein UNION ALL über alle drei Statements machen

SELECT all.Username, all.object_id, all.Date FROM (

SELECT j.username,j.object_id,j.date,
    -- c.catid, ?? Brauchst du die?
   c.title AS Title
FROM jos_jcomments AS j
INNER JOIN jos_content AS c ON c.id = j.object_id
WHERE j.published = 1
AND j.object_group = 'com_content'  

UNION ALL 
SELECT j.username, j.object_id, j.date, g.imgtitle
FROM jos_jcomments AS j
INNER JOIN jos_joomgallery AS g ON g.id = j.object_id
-- WHERE  j.published =1
AND j.object_group = 'com_joomgallery'  

UNION ALL

SELECT j.username, j.object_id, j.date, v.title
FROM jos_jcomments AS j
INNER JOIN jos_hwdvidsvideos AS v ON v.id = j.object_id
WHERE  j.published =1
AND j.object_group = 'com_hwdvideoshare_v'  
) all

where All.date j.date BETWEEN  '2010-07-20 18:09:37' and 2010-07-21 00:09:37'  

ORDER BY all.date DESC
[ungetestete Skizze]

...wie gesagt, als Proof-of-Concept.
Sollte das vom Ergebnis her plausibel, aber grotteninperformat sein, dann machen wir mir JOINs ein bisschen wat Flotteres.

Grüße
Biber
Member: Igather
Igather Jul 21, 2010 at 20:15:56 (UTC)
Goto Top
Vielen Dank erstmal an dich Biber!

Also ich hoffe ich versteh das... *grübel*

Ahso, erstmal, das sind nicht die Kommentare eines Users, sondern diese Liste im Endergebnis soll sowas sein wie die letzten Beiträge in einem Forum, nur eben mit den Kommentaren.

Die catid aus der Tabelle jos_content brauche ich um den Link zu dem Artikel bei der Ausgabe erzeugen zu können.

Pro Kommentar kann ja nur einer der Fälle zutreffen, das Kommentar ist entweder ein Bilder, Artikel oder Videokommentar.

Die Bedingung mit dem Datum gilt für die Tabelle der Kommentare, ich möchte ja alle Kommentare herausholen, die zwischen jetzt und dem Datum des letzten Logins des Users vorhanden sind, also kann man das all.date rausnehmen? Das Datum des Artikels, Bildes oder Videos ist ja uninteressant, da hier lediglich die Kommentare benötigt werden.

Leider habe ich dein Query nicht testen können, es meldet mir einen Syntax-Fehler bei der Zeile 22, den ich aufgrund mangelnder Kenntnisse mit dem UNION nicht herausfinden kann. Mache mich erstmal schlau zum UNION...
Member: Igather
Igather Jul 21, 2010 at 20:34:08 (UTC)
Goto Top
Ich glaub es ja nicht, es scheint zu funktionieren!

Das UNION ist nicht das richtige, soweit ich das überblicken kann, sondern ein LEFT JOIN anstatt des inner joins. Laut tutorial funktioniert ein LEFT join nämlich auch dann, wenn bestimmte Felder nicht ausgefüllt sind.

        $query = "SELECT j.username,j.date,j.object_group,j.object_id,cont.catid,cont.title AS artitle,gal.imgtitle AS imgtitle,vids.title AS vidtitle"  
               . "\n FROM (#__jcomments AS j"  
               . "\n LEFT JOIN #__content AS cont ON cont.id = j.object_id)"  
               . "\n LEFT JOIN #__joomgallery AS gal ON gal.id = j.object_id"  
               . "\n LEFT JOIN #__hwdvidsvideos AS vids ON vids.id = j.object_id"  
               . "\n WHERE j.date < '$now'"  
               . "\n AND j.date > '$userlastvisit'"  
               . "\n AND j.published = 1 "  
               . "\n AND (cont.sectionid = '3' OR j.object_group = 'com_hwdvideoshare_v' OR j.object_group = 'com_joomgallery')"  
               . "\n ORDER BY j.date DESC"  
             ;
        $db->setQuery( $query );

Diese Abfrage gibt zwar bei einem Query in phpmyadmin ein unschönes ergebnis, da z.B. ein Kommentar bei einem Video auch mit einem falschen Artikeltitel gefüllt wird.
Da ich aber in der Ausgabe des Moduls mit einem Switch-Case die object_group abfrage, ignoriere ich einfach den falschen titel und es funktioniert! Jedenfalls bei meinen ersten Tests.

Ist ja unglaublich!!!

Ich glaube das ist unschön programmiert im hinblick auf das SQL, da nicht-benötigte Einträge aus der DB geholt werden, aber ist das wichtig?
Member: Biber
Biber Jul 22, 2010 at 09:49:45 (UTC)
Goto Top
Moin Igather,

sorry, dann hatte ich die Fragestellung doch falsch verstanden - ich war davon ausgegangen, dass in der Tat ein "sowohl als auch" bei Videos, Bildern und Artikeln da sein könnte.

Von daher vergiss diesen kleinen Exkurs mit UNION.
Der (erste) Syntaxfehler in meiner ungetesteten Skizze kommt sicherlich duch die Zeilen 15-17 zustande.
Da habe ich schlauerweise gleich die Zeile mit dem Schlüsselwort WHERE auskommentiert... das kann nicht fliegen.

Grüße
Biber
Member: Igather
Igather Jul 22, 2010 at 19:53:21 (UTC)
Goto Top
Hi!

Das macht wirklich garnichts, vielen Dank nochmals für deine Hilfe!!!
Member: Biber
Biber Jul 22, 2010 at 20:08:19 (UTC)
Goto Top
Moin lgather,

dann wenigstens noch ein Nachklapp zur Schadensbegrenzung:

        
                $query = "SELECT j.username,j.date,j.object_group,j.object_id,cont.catid,"   
                         "\n CASE j.object_group "  
                         "\n WHEN 'com_hwdvideoshare_v' Then vids.titel "  
                         "\n WHEN 'com_joomgallery' Then gal.imgtitle "  
                         "\n ELSE cont.title " END As Title  
	               . "\n FROM (#__jcomments AS j"  
	               . "\n LEFT JOIN #__content AS cont ON cont.id = j.object_id)"  
	               . "\n LEFT JOIN #__joomgallery AS gal ON gal.id = j.object_id"  
	               . "\n LEFT JOIN #__hwdvidsvideos AS vids ON vids.id = j.object_id"  
	               . "\n WHERE j.date < '$now'"  
	               . "\n AND j.date > '$userlastvisit'"  
	               . "\n AND j.published = 1 "  
	               . "\n AND (cont.sectionid = '3' OR j.object_group = 'com_hwdvideoshare_v' OR j.object_group = 'com_joomgallery')"  
	               . "\n ORDER BY j.date DESC"  
	             ;
        $db->setQuery( $query );

...um deinen ursprünglichen CASE-Käse-Gedanken neu zu beleben...

[auch wieder ungetestet... aber in dem Statement findest du glaub ich durch]

Grüße
Biber