MySQL Abfrage bei m:1 Verbindung
Hallo liebe Community,
ich sitze gerade vor einem SQL-"Problem" - Ich möchte eine Abfrage über mehrere Tabellen machen, wobei diese Tabellen auf eine einzige zugreifen.
Ich habe vier Tabellen mit technischen Spezifikationen von Gerät, Name Soundkarte, Name Grafikkarte, Name Nettzwerkkarte. "Dummerweise" sind die jeweiligen Herstellerbezeichnungen in einer anderen Tabelle gespeichert. Ich habe es bisher nicht geschafft, aller Infos zu einem Gerät - also Gerät + Hersteller, Grafikkarte + Hersteller, ... in einer einzigen SQL Query abzufackeln. Geht das überhaupt?
Herzlichen Dank und beste Grüße
ich sitze gerade vor einem SQL-"Problem" - Ich möchte eine Abfrage über mehrere Tabellen machen, wobei diese Tabellen auf eine einzige zugreifen.
Ich habe vier Tabellen mit technischen Spezifikationen von Gerät, Name Soundkarte, Name Grafikkarte, Name Nettzwerkkarte. "Dummerweise" sind die jeweiligen Herstellerbezeichnungen in einer anderen Tabelle gespeichert. Ich habe es bisher nicht geschafft, aller Infos zu einem Gerät - also Gerät + Hersteller, Grafikkarte + Hersteller, ... in einer einzigen SQL Query abzufackeln. Geht das überhaupt?
Herzlichen Dank und beste Grüße
Bitte markiere auch die Kommentare, die zur Lösung des Beitrags beigetragen haben
Content-ID: 281312
Url: https://administrator.de/forum/mysql-abfrage-bei-m-1-verbindung-281312.html
Ausgedruckt am: 05.02.2025 um 00:02 Uhr
37 Kommentare
Neuester Kommentar
Also wenn ich dich richtig verstehe suchst du das hier:
SELECT t.name,
tabelle_hersteller.name
FROM (
SELECT tabelle_geraet.name,
tabelle_geraet.hersteller_id
FROM tabelle_geraet
UNION ALL
SELECT tabelle_soundkarten.name,
tabelle_soundkarten.hersteller_id
FROM tabelle_soundkarten
UNION ALL
SELECT tabelle_grafikkarten.name,
tabelle_grafikkarten.hersteller_id
FROM tabelle_grafikkarten
UNION ALL
SELECT tabelle_netzwerkkarten.name,
tabelle_netzwerkkarten.hersteller_id
FROM tabelle_netzwerkkarten
) t
LEFT JOIN tabelle_hersteller ON tabelle_hersteller.id = t.hersteller_id
Zitat von @IGEL.Daniel:
soweit war ich auch schon. Aber ich kann ja eine Tabelle nicht mehrmals (left) joinen, oder?
Mit Subqueries oder Aliasen schon ... siehe Ukulele's Post.soweit war ich auch schon. Aber ich kann ja eine Tabelle nicht mehrmals (left) joinen, oder?
Du kannst Joinen so oft du willst nur musst du dann mit Tabellen Aliasen arbeiten. Ein Join wird dir die Daten aber immer "nebeneinander" stellen, UNION hängt dir Tabellen "unten an" (bei gleicher Spaltenanzahl und Datentyp). Ich kenne deine Tabellen nicht, vieleicht brauchst du auch einfach nur mehrere Joins und deine Komponenten wie Sound- und Grafikkarte sind mit der Gerätetabelle verknüpft.
Dein erster Select den du mit UNION ALL verkettest hat auch nur eine Spalte, kann so nicht funktionieren. Also so würde es schonmal funktionieren:
Wenn du aber sowieso schreibst " ich bräuchte es für jedes Gerät in einer Reihe" dann hast du vermutlich eine ganz andere Struktur und suchst eher das hier:
usw.
SELECT
t.designation, M.name
FROM
(
SELECT
DGC.designation, DGC.manufacturers_id
FROM
glpi_devicegraphiccards AS DGC
UNION ALL
SELECT
DSC.designation, DSC.manufacturers_id
FROM
glpi_devicesoundcards AS DSC
UNION ALL
SELECT
DNC.designation, DNC.manufacturers_id
FROM
glpi_devicenetworkcards AS DNC
) t
LEFT JOIN
glpi_manufacturers AS M
ON
t.manufacturers_id = M.id
SELECT C.name,
MC.name,
DGC.designation,
MDGC.name,
DSC.designation,
MDSC.name
FROM glpi_computers C
LEFT JOIN glpi_manufacturers MC ON MC.id = C.manufacturers_id
LEFT JOIN glpi_devicegraphiccards DGC ON DGC.id = C.graphiccard_id
LEFT JOIN glpi_manufacturers MDGC ON MDGC.id = DGC.manufacturers_id
LEFT JOIN glpi_devicesoundcards DSC ON DSC.id = C.soundcard_id
LEFT JOIN glpi_manufacturers MDSC ON MDSC.id = DGC.manufacturers_id
Deine Joins scheinen nicht sinnvoll. Der pk C.id wird nicht dem DGC.manufactures_id entsprechen, du joinst auf die falschen IDs. In der Haupttabelle glpi_computers muss es Fremdschlüssel geben die sagen welche Soundkarte, welche Grafikkarte, etc. verbaut ist. Oder du hast keine 1:n sondern eine n:m Beziehung, also noch eine Zwischentabelle.
SELECT C.name,
MC.name AS Hersteller,
DGC.designation AS Grafikkarte,
MDGC.name AS Hersteller,
DSC.designation AS Soundkarte,
MDSC.name AS Hersteller
FROM glpi_computers C
-- Hersteller des Computers joinen
LEFT JOIN glpi_manufacturers MC ON C.manufacturers_id = MC.id
-- verbaute Grafikkarte joinen (Fremdschlüssel Grafikkarte = Primärschlüssel Grafikkarten-Tabelle)
LEFT JOIN glpi_devicegraphiccards DGC ON C.graphiccard_id = DGC.id
-- Hersteller der verbauten Grafikkarte joinen
LEFT JOIN glpi_manufacturers MDGC ON DGC.manufacturers_id = MDGC.id
--- das Selbe für Soundkarte
LEFT JOIN glpi_devicesoundcards DSC ON C.soundcard_id = DSC.id
LEFT JOIN glpi_manufacturers MDSC ON DSC.manufacturers_id = MDSC.id
-- weitere Komponenten
Bisher hast du nicht erwähnt ob es eine Zwischentabelle zu Soundkarten und Netzwerkkarten gibt, also eine n:m Beziehung, oder eine 1:n Beziehung ist. Da das Query schon echt lang ist würde ich das einmal Beispielhaft aufzeigen.
1:n Beziehung
n:m Beziehung
Durch ORDER BY und LIMIT kommt jetzt der erste Datensatz in der Zwischentabelle und wird als Verknüpfung für die erste Soundkarte genommen, das gleiche für die Zweite. Es könnte natürlich eine dritte Soundcard geben die jetzt nicht mehr angezeigt würde. Du siehst also die Lösung skaliert nicht besonders gut, in diesem Fall dürfte sie aber Sinn ergeben.
Alternativ gibt es bei MySQL glaube ich GROUP_CONCAT() und eventuell auch PIVOT. Ich bin aber nicht gut mit MySQL Syntax das müsstest du dir mal selber anschauen.
1:n Beziehung
SELECT c.name AS Computer,
s1.name AS Soundcard1,
s2.name AS Soundcard2
FROM computers c
LEFT JOIN ( SELECT fk_computer,
name
FROM soundcards
ORDER BY pk
LIMIT 1 ) s1
ON c.pk = s1.fk_computer
LEFT JOIN ( SELECT fk_computer,
name
FROM soundcards
ORDER BY pk
LIMIT 1 OFFSET 1 ) s2
ON c.pk = s2.fk_computer
n:m Beziehung
SELECT c.name AS Computer,
s1.name AS Soundcard1,
s2.name AS Soundcard2
FROM computers c
LEFT JOIN ( SELECT fk_computer,
fk_soundcard
FROM computers_soundcards
ORDER BY pk
LIMIT 1 ) z1
ON c.pk = z1.fk_computer
LEFT JOIN soundcards s1
ON z1.fk_soundcard = s1.pk
LEFT JOIN ( SELECT fk_computer,
fk_soundcard
FROM computers_soundcards
ORDER BY pk
LIMIT 1 OFFSET 1 ) z2
ON c.pk = z2.fk_computer
LEFT JOIN soundcards s2
ON z2.fk_soundcard = s2.pk
Durch ORDER BY und LIMIT kommt jetzt der erste Datensatz in der Zwischentabelle und wird als Verknüpfung für die erste Soundkarte genommen, das gleiche für die Zweite. Es könnte natürlich eine dritte Soundcard geben die jetzt nicht mehr angezeigt würde. Du siehst also die Lösung skaliert nicht besonders gut, in diesem Fall dürfte sie aber Sinn ergeben.
Alternativ gibt es bei MySQL glaube ich GROUP_CONCAT() und eventuell auch PIVOT. Ich bin aber nicht gut mit MySQL Syntax das müsstest du dir mal selber anschauen.
Möglich, vermutlich hast du einen kleinen Fehler eingebaut.
ORDER BY = Nach was du sortierst ist eigentlich egal, wichtig ist das eine Sortierung definiert wird sonst ist die DB nicht "gezwungen" die Daten jedes mal gleich zu sortieren.
LIMIT = begrenzt die Anzahl der Datensätze auf 1, wählt also den ersten.
LIMIT mit OFFSET = begrenzt auch die Anzahl auf 1, verwirft aber den ersten und nimmt dann den zweiten.
Hast du vieleicht OFFSET vergessen? Ansonsten musst du nochmal den Code posten.
ORDER BY = Nach was du sortierst ist eigentlich egal, wichtig ist das eine Sortierung definiert wird sonst ist die DB nicht "gezwungen" die Daten jedes mal gleich zu sortieren.
LIMIT = begrenzt die Anzahl der Datensätze auf 1, wählt also den ersten.
LIMIT mit OFFSET = begrenzt auch die Anzahl auf 1, verwirft aber den ersten und nimmt dann den zweiten.
Hast du vieleicht OFFSET vergessen? Ansonsten musst du nochmal den Code posten.
Oh ja das habe ich nicht bedacht. Bei MSSQL würde ich mit ROW_NUMBER und PARTION BY arbeiten und etwas anders vorgehen, das gibt es bei MySQL aber nicht. LIMIT reicht hier nicht aus.
Folgendes passiert: Du Joinst die Netzwerkkartentabelle aus einem Subquery das die Anzahl der Zeilen auf 1 reduziert. Allerdings gibt es keine Einschränkungen bei der Auswahl so das der verbleibende Datensatz nicht zwigend zu dem Hauptdatensatz (dem Cumputer) passt. Ergo kriegst du maximal einmal eine richtige Zuordnung.
Du kannst jetzt entweder mit WHERE weiter einschränken:
Damit machst du im Subselect einen abgleich mit einer Spalte außerhalb des Subselects. Diese Bedingung ist also defakto doppelt, du könntest also auch deinen ganzen Subselect (mit dem WHERE und ohne LEFT JOIN und ON) in die Spaltenliste schreiben.
Es ginge auch eleganter aber wie schon erwähnt, MySQL ist nicht so mein Alltag.
Folgendes passiert: Du Joinst die Netzwerkkartentabelle aus einem Subquery das die Anzahl der Zeilen auf 1 reduziert. Allerdings gibt es keine Einschränkungen bei der Auswahl so das der verbleibende Datensatz nicht zwigend zu dem Hauptdatensatz (dem Cumputer) passt. Ergo kriegst du maximal einmal eine richtige Zuordnung.
Du kannst jetzt entweder mit WHERE weiter einschränken:
LEFT JOIN
(
SELECT
DNC.designation, M.name, IDNC.items_id, IDNC.id
FROM
glpi_items_devicenetworkcards AS IDNC
LEFT JOIN
glpi_devicenetworkcards AS DNC
ON
IDNC.devicenetworkcards_id = DNC.id
LEFT JOIN
glpi_manufacturers AS M
ON
DNC.manufacturers_id = M.id
WHERE IDNC.items_id = C.id
ORDER BY
IDNC.id
LIMIT 1
) NC1
ON
C.id = NC1.items_id
Es ginge auch eleganter aber wie schon erwähnt, MySQL ist nicht so mein Alltag.
Hm noch son Ding was in MySQL nicht geht...
Das Beste wird sein du baust dir für die Tabellen glpi_items_devicenetworkcards und glpi_items_devicesoundcards jeweils eine Sicht in der du ROW_NUMBER und PARTION BY "simulierst". Das wäre in etwa so:
Quelle: http://blog.sqlauthority.com/2014/03/09/mysql-reset-row-number-for-each ...
Ich kann das leider nicht testen.
Damit joinst du dann etwa so:
Das Beste wird sein du baust dir für die Tabellen glpi_items_devicenetworkcards und glpi_items_devicesoundcards jeweils eine Sicht in der du ROW_NUMBER und PARTION BY "simulierst". Das wäre in etwa so:
CREATE VIEW view_glpi_items_devicenetworkcards
SELECT @row_number := ( CASE
WHEN @db_names = db_names
THEN @row_number + 1
ELSE 1
END ) AS row_number,
@db_names := items_id AS db_names
items_id,
devicenetworkcards_id
FROM glpi_items_devicenetworkcards,
( SELECT @row_number := 0,
@db_names := '') AS t
Ich kann das leider nicht testen.
Damit joinst du dann etwa so:
SELECT C.id,
NC1.designation AS 'Netzwerkkarte 1',
NC1m.name AS 'Hersteller',
NC2.designation AS 'Netzwerkkarte 2',
NC2m.name AS 'Hersteller'
FROM glpi_computers AS C
-- Netzwerkkarten
LEFT JOIN view_glpi_items_devicenetworkcards NC1z
ON C.id = NC1z.items_id
AND NC1z.row_number = 1
LEFT JOIN glpi_devicenetworkcards NC1
ON NC1z.devicenetworkcards_id = NC1.id
LEFT JOIN glpi_manufacturers NC1m
ON NC1.manufacturers_id = NC1m.id
LEFT JOIN view_glpi_items_devicenetworkcards NC2z
ON C.id = NC2z.items_id
AND NC2z.row_number = 2
LEFT JOIN glpi_devicenetworkcards NC2
ON NC2z.devicenetworkcards_id = NC2.id
LEFT JOIN glpi_manufacturers NC2m
ON NC2.manufacturers_id = NC2m.id
Nein nicht ganz. Die View soll der Tabelle glpi_items_devicenetworkcards nur eine Zeilennummer hinzufügen die die Netzwerkkarten pro Computer durchzählt und dann wieder auf Null springt. Normalerweise würde ich das mit
machen. Diese Zeilennummer kann ich dann im Join als Kriterium verwenden.
ROW_NUMBER() OVER (ORDER BY devicenetworkcards_id PARTITION BY items_id)
Moin IGEL.Daniel,
mal rein interessehalber....
Was wäre denn der signifikante Unterschied, wenn du die letzte Zeile "GROUP BY C.id" ersatzlos streichst?
Ich sehe keine Aggregat-Funktionen und auch keinen Bedarf dafür.
Frisst IMHO doch nur sinnlos Resourcen ... dann doch lieber die Kohle spenden statt zu verbrennen.
Spendenkonto-Nummern kann ich bei Bedarf nachreichen.
Grüße
Biber
mal rein interessehalber....
Was wäre denn der signifikante Unterschied, wenn du die letzte Zeile "GROUP BY C.id" ersatzlos streichst?
Ich sehe keine Aggregat-Funktionen und auch keinen Bedarf dafür.
Frisst IMHO doch nur sinnlos Resourcen ... dann doch lieber die Kohle spenden statt zu verbrennen.
Spendenkonto-Nummern kann ich bei Bedarf nachreichen.
Grüße
Biber
Moin IGEL.Daniel und ukulele-7,
da muss ich jezz' mal bei einem Frühstückskäffchen drüber nachdenken, wieso das passiert....
Nichtsdestotrotz - wenn mehrere identische Sätze eingedampft werden sollen, dann wäre das probate Mittel
Wie ukulele-7 schon schrieb -eigentlich sollte eine Syntax-Fehlermeldung kommen und kein Resultset.
Ich hol erstma' Kaffee.
Grüße
Biber
da muss ich jezz' mal bei einem Frühstückskäffchen drüber nachdenken, wieso das passiert....
Nichtsdestotrotz - wenn mehrere identische Sätze eingedampft werden sollen, dann wäre das probate Mittel
SELECT DISTINCT
C.name, MF.name AS 'Hersteller', CM.name AS 'Modell',
--- <und der Rest der obigen Query...
-- aber ohne GROUP BY>
Wie ukulele-7 schon schrieb -eigentlich sollte eine Syntax-Fehlermeldung kommen und kein Resultset.
Ich hol erstma' Kaffee.
Grüße
Biber
Dann solltest du wirklich mit DISTINCT arbeiten und nicht mit GROUP BY, falls dieses Verhalten bei MySQL geändert wird.
http://dev.mysql.com/doc/refman/5.0/en/group-by-handling.html
http://dev.mysql.com/doc/refman/5.0/en/group-by-handling.html