MySQL: mehrere SQL-Abfragen anstatt Joins sinnvoll?

klausschreiber

Well-known member
ID: 162475
L
6 Mai 2006
247
8
Hallo,

ich habe mal wieder eine Frage zu MySQL.

Ich habe einen Ordner, in dem können mehrere Dateien liegen. Außerdem kann der Ordner mehreren Kategorien zugeordnet sein.
Das hieße ja, dass indirekt auch jede Datei jeder Kategorie zugeordnet ist. Wenn ich das nun per JOINS löse, wäre die Ausgabe bei z.B. 2 Dateien und 3 Kategorien folgendermaßen:
Code:
Verzeichnis1 (Name, Beschreibung usw.)         Datei1         Kategorie1
Verzeichnis1 (Name, Beschreibung usw.)         Datei1         Kategorie2
Verzeichnis1 (Name, Beschreibung usw.)         Datei1         Kategorie3
Verzeichnis1 (Name, Beschreibung usw.)         Datei2         Kategorie1
Verzeichnis1 (Name, Beschreibung usw.)         Datei2         Kategorie2
Verzeichnis1 (Name, Beschreibung usw.)         Datei2         Kategorie3
Es werden also 2*3 Zeilen ausgegeben. Bei 4 Kategorien und 4 Dateien wären es 4*4=16 Zeilen.

Der Dateiinhalt ist nicht in der Datenbank abgelegt, sondern lediglich die Verknüpfung zur Datei, jedoch enthält jedes Verzeichnis ein Textfeld, was ja theoretisch schon mehrere 100 und mehr Zeichen aufnehmen kann (auch wenn in meinem Fall das Textfeld vermutlich nicht sooo wahnsinnig viele Zeichen enthält.

Dass ein Verzeichnis mehr als 4 Kategorien zugeordnet ist oder mehr als 4 Dateien enthält, ist eher unwahrscheinlich, auch wenn es theoretisch möglich wäre. Aber auch bei 4 Kategorien und 4 Dateien würde z.B. das Textfeld 15 mal unötig ausgegeben.

Ist es trotzdem üblich/richtig, die Abfrage mittels Join zu machen (und dann mittels PHP für eine leichtere Verwendung z.B. in ein mehrdimensionales Array zu konvertieren), oder gibt es da bessere Wege (z.B. Aufteilung in 2 oder 3 Abfragen)?

Gruß,
Klaus
 
In diesem Fall würde ich die Beschreibung nur einmal ausgeben und die Zeilen dann via rowspan verknüpfen. Das erfordert dann eine Sortierung nach der Verzeichnis-ID.

Greetz

paddya
 
Danke für deine Antwort. Aber gibt es in MySQl die Möglichkeit eines 'rowspan's? Ich habe über Google nichts dazu gefunden. Ich suche nicht nach einer Möglichkeit, das Ganze in HTML geschickt auszugeben (dort soll es gar nicht als Tabelle ausgegeben werden, sondern untereinander), sondern meine Frage ist, ob die MySQL Abfrage in dieser Weise sinnvoll/richtig ist, oder ob die Redundanz einzelner Spalten bei der Ausgabe nur unnötig Rechenzeit benötigt und es eine bessere Lösung gibt.
 
Achso, okay. Dann wäre es evtl. sinnvoll, das ganze nach den Verzeichnissen zu gliedern und dementsprechend dann zwei Abfragen (einmal die Verzeichnisse und einmal die Dateien aus der DB holen) zu machen. Ausgeben würde ich es dann mit zwei verschachtelten Schleifen.

Greetz

paddya
 
danke für eure Antworten.

@paddya:
Meinst du 2 oder 3 Abfragen? Ich hole mit der Abfrage ja ein Verzeichnis (mit Beschreibung usw.), mehrere Kategorien und mehrere Dateien? Oder meinst du damit, ich soll die Kategorien extra holen und dann das Verzeichnis und die Dateien zusammen?

@EB_Stefan:
Ich versuchs mal, aber weißt nicht, ob es so verständlicher ist.
Also ganz genau sähe die Abfrage so aus:
Code:
[B][COLOR=Magenta]SELECT[/COLOR][/B] mc_folders.shortDescription, [COLOR=Red]DATE_FORMAT[/COLOR]( mc_folders.date,  [COLOR=SeaGreen]'%d.%m.%y'[/COLOR]  )  [COLOR=Magenta][B]AS[/B][/COLOR] FolderDate, mc_folders.description, mc_files.name [COLOR=Magenta][B]AS[/B][/COLOR] file_name, mc_files.rights, [COLOR=Red]DATE_FORMAT[/COLOR]( mc_files.date,  [COLOR=SeaGreen]'%d.%m.%y'[/COLOR]  )  [B][COLOR=Magenta]AS[/COLOR][/B] FileDate, mc_categories.category_id, mc_categories.name [COLOR=Magenta][B]AS[/B][/COLOR] category_name, mc_categorygroups.name [B][COLOR=Magenta]AS[/COLOR][/B] group_name
[COLOR=Magenta][B]FROM[/B][/COLOR] mc_files
[B][COLOR=Magenta]JOIN[/COLOR][/B] mc_folders [B][COLOR=Magenta]USING[/COLOR][/B] ( folder_id ) 
[B][COLOR=Magenta]JOIN[/COLOR][/B] mc_folders_categories [COLOR=Magenta][B]USING[/B][/COLOR] ( folder_id ) 
[COLOR=Magenta][B]JOIN[/B][/COLOR] mc_categories [B][COLOR=Magenta]USING[/COLOR][/B] ( category_id ) 
[COLOR=Magenta][B]JOIN[/B][/COLOR] mc_categorygroups [COLOR=Magenta][B]USING[/B][/COLOR] ( categorygroup_id ) 
[B][COLOR=Magenta]WHERE[/COLOR][/B] mc_folders.folder_id = $FolderID
Du kannst sie dir aber auch einfach so denken:
Code:
[B][COLOR=Magenta]SELECT[/COLOR][/B] mc_folders.shortDescription, [COLOR=Red]DATE_FORMAT[/COLOR]( mc_folders.date,  [COLOR=SeaGreen]'%d.%m.%y'[/COLOR]  )  [COLOR=Magenta][B]AS[/B][/COLOR] FolderDate, mc_folders.description, mc_files.name [COLOR=Magenta][B]AS[/B][/COLOR] file_name, mc_files.rights, [COLOR=Red]DATE_FORMAT[/COLOR]( mc_files.date,  [COLOR=SeaGreen]'%d.%m.%y'[/COLOR]  )  [B][COLOR=Magenta]AS[/COLOR][/B] FileDate, mc_categories.category_id, mc_categories.name [COLOR=Magenta][B]AS[/B][/COLOR] category_name
[COLOR=Magenta][B]FROM[/B][/COLOR] mc_files
[B][COLOR=Magenta]JOIN[/COLOR][/B] mc_folders [B][COLOR=Magenta]USING[/COLOR][/B] ( folder_id ) 
[B][COLOR=Magenta]JOIN[/COLOR][/B] mc_folders_categories [COLOR=Magenta][B]USING[/B][/COLOR] ( folder_id ) 
[COLOR=Magenta][B]JOIN[/B][/COLOR] mc_categories [B][COLOR=Magenta]USING[/COLOR][/B] ( category_id )  
[B][COLOR=Magenta]WHERE[/COLOR][/B] mc_folders.folder_id = $FolderID
Tabellen:
mc_files: file_id, name, date, rights, folder_id
mc_folders: folder_id, shortDescription, description, date, user_id
mc_folders_categories: folder_category_id, folder_id, category_id
mc_categories: category_id, name, categorygroup_id
 
1. Aus welchem Grund verwendest du using?
2. Willst du ein kartesisches Produkt erstellen oder warum hast du keine Join-Bedingungen?
 
danke für deine Antwort.

zu 1.:
Ich finde
Code:
USING (id)
übersichtlicher als
Code:
ON tbl1.id = tbl2.id
Das ist doch das Gleiche, oder?

zu 2.:
Ich sehe gerade selber, dass ich statt "INNER JOIN" nur "JOIN" geschrieben habe. Würde deshalb temporär ein kartesisches Produkt entstehen (funktionieren tut ja Beides), oder habe ich einen anderen Fehler in der Syntax?
 
Das ist doch das Gleiche, oder?
ja ist es, finde ich nur unflexibler, und wenn man die zugrundeligene db-struktur net kennt, ist es viel schwerer zu erkennen, da man nicht weiß was du mit was verknüpfst.

Ich sehe gerade selber, dass ich statt "INNER JOIN" nur "JOIN" geschrieben habe. Würde deshalb temporär ein kartesisches Produkt entstehen (funktionieren tut ja Beides), oder habe ich einen anderen Fehler in der Syntax?
ein kartesisches produkt ist wenn du keine join-bedingungen hast, hatte dein using falsch interpretiert, ohne db-sturktur net so einfach.
Und ein INNER Join produziert ohne bedingung ein kartesisches Produkt, mit Bedingung (deinem Using) gibt es keinen Sinn es Inner Join zu nennen, informier dich doch über die verschiedenen Join Typen.
 
danke für deine Antwort.

Jo, es wird kein kartesisches Produkt ausgegeben. Die Frage war, ob ein temporäres kartesisches Produkt erstellt wird, wie bei Verknüpfung mittels "WHERE tabelle1.is =tabelle2.id", weil ich mir nicht recht wusste, wo oder wodurch ein kartesisches Produkt bei meinem Code erstellt werden sollte. Aber das hat sich ja jetzt geklärt.

"INNER JOIN" wollte ich nehmen, weil damit ja (wenn ich nicht komplett falsch liege) nur Datensätze ausgegeben werden, die in beiden Tabellen vorkommen (also halt ein Gegenstück haben). Das "INNER" habe ich dann aber wohl vergessen, mit hinzuschreiben (bei allen meinen SQL-Abfragen). Bei "JOIN" ohne "INNER" hst es, wie du ja auch meintest, das richtige Ergebnis ausgegeben. Aber wozu gibt es dann noch "INNER"? Nur für den Fall, dass man ein Kartesisches Produkt will? Ich habe dazu leider nichts gefunden.

Ich habe mir jetzt aber auch deinen Link nochmal durchgelesen (hatte ich zwar vor ein ppar Tagen schonmal, aber anscheind nicht gründlich genug:-?) und jetzt bin ich total durcheinander. Dort steht einmal:
  • If there is no matching row for the right table in the ON or USING part in a LEFT JOIN, a row with all columns set to NULL is used for the right table. You can use this fact to find rows in a table that have no counterpart in another table:
Und einmal:
  • The USING(column_list) clause names a list of columns that must exist in both tables. If tables a and b both contain columns c1, c2, and c3, the following join compares corresponding columns from the two tables:
    Code:
    a LEFT JOIN b USING (c1,c2,c3)
  • The NATURAL
    JOIN of two tables is defined to be semantically equivalent to an INNER JOIN or a LEFT JOIN with a USING clause that names all columns that exist in both tables.


Ein "LEFT JOIN" gibt ja auch alle Datensätze der linken Tabelle aus, die kein Gegenstück in der rechten Tabelle haben. Im ersten Teil wird gesagt, dies gilt sowohl für "ON" als auch für "USING". Im zweiten Teil wird dann auf einmal gesagt, "LEFT JOIN" in Kombination mit "USING" = "INNER JOIN"? Ja was denn nun? Oder verstehe ich gerade was falsch?​
 
Unabhängig von den JOIN-Fragen würde ich Dir vorschlagen, für die Kategorien group_concat() zu verwenden. Damit reduzierst Du die Anzahl der zurückgegebenen Zeilen auf die Anzahl der verfügbaren Dateien und kannst später dennoch in Deiner Logik relativ simpel auf alle Kategorien zugreifen.
 
Meinst du 2 oder 3 Abfragen? Ich hole mit der Abfrage ja ein Verzeichnis (mit Beschreibung usw.), mehrere Kategorien und mehrere Dateien? Oder meinst du damit, ich soll die Kategorien extra holen und dann das Verzeichnis und die Dateien zusammen?

Drei Abfragen:
  1. Verzeichnisse holen
  2. Alle Kategorien holen, die einem Verzeichnis zugeordnet sind
  3. Alle Dateien holen

Daraus kannst du dann deine Arrays zusammenbauen. Folgende Array-Struktur wäre denkbar:
PHP:
array(0 => array('kategorien' => array('bla', 'blub'), 'dateien' => array('datei1.pdf', 'datei2.pdf', 'datei3.pdf'), 'beschreibung' => 'Lorem ipsum', 'name' => 'Verzeichnis XY'))

Das könntest du dann wunderbar durchlaufen.

Edit: Eventuell ist es sinnvoll statt fortlaufenden Array-Schlüsseln die ID des Verzeichnisses zu nehmen (um die Kategorien und Dateien besser einordnen zu können).

Also:
PHP:
array($verzeichnis_id => array('kategorien' => array('bla', 'blub'), 'dateien' => array('datei1.pdf', 'datei2.pdf', 'datei3.pdf'), 'beschreibung' => 'Lorem ipsum', 'name' => 'Verzeichnis XY'))

Greetz

paddya
 
Zuletzt bearbeitet:
ok, danke euch beiden.

@tleilax:
Diese Möglichkeit kannte ich noch gar nicht. Ist eigentlich gar keine schlechte Idee.

@paddya:
Eigentlich heißt es ja meistens, je weniger Abfragen, desto besser/schneller. Ist es in diesem Fall trotzdem besser, das Ganze aufzuteilen, anstatt es z.B. so zu Lösen, wie tleilax es vorgeschlagen hat?
Bezüglich Array:
Jo, gute Idee, werde ich wohl so ähnlich machen, genaugenommen hatte ich es so in der Art gedacht.
PHP:
$ausgabe['Verzeichnis_ID'] = $verzeichnis_id;
$ausgabe['Verzeichnisname'] = $verzeichnisname;
$ausgabe['Beschreibung'] = $beschreibung;
$ausgabe['Dateien'][]['Datei_ID'] = $datei_id;
$ausgabe['Dateien'][]['Dateiname'] = $dateiname;
$ausgabe['Kategorien'][]['Kategorie_ID'] = $kategorie_id;
$ausgabe['Kategorien'][]['Kategoriename']=$kategoriename;
Also so halt ungefähr, weil ich ja immer nur ein bestimmtes Verzeichnis holen muss.

edit:
Stimmt
Code:
tbl1 JOIN tbl2 USING (id)
nun wirklich, oder muss es bei Joins mit USING irgendwie Anders heißen? (Die Beschreibung bei mysql.com hat mich ja gestern etwas verwirrt). Also bei einer Abfrage, wo halt nur Zeilen ausgegeben werden sollen, die in beiden Tabellen vorkommen.
 
Zuletzt bearbeitet:

Ähnliche Themen