derseso
Goto Top

MySQL Abfrage über 2 Tabellen

Hallo,
irgendwie steh ich mächtig auf dem Schlauch (liegts am Montag?).

Ich habe 2 MySQL-Tabellen:
Tab "Produkt" ... | id | name | ...
Tab "Komponenten ... | id | id_produkt | nummer | name | ...

In der Tab "Produkt" stehen die Produkte.
In der Tab "Komponenten" stehen die einzelnen Teile, aus denen ein Produkt besteht.

Jetzt möchte ich alle Produkte angezeigt bekommen, die mindestens aus bestimmten Komponenten/Teilen bestehen.

Bsp.:
- Produkt EINS besteht aus den Teilen mit der "nummer" 123, 456, 789, 134, 188
- Produkt ZWEI besteht aus den Teilen mit der "nummer" 123, 456, 677, 777, 223, 334
- Produkt DREI besteht aus den Teilen mit der "nummer" 123, 456, 781, 134, 333, 431

Ich suche nach den Produkten, die alle mindestens aus den Teilen mit der "nummer" 123, 456 und 134 bestehen.
Somit sollte das Ergebnis die Produkte EINS und DREI sein.

Wie setze ich die MySQL-Abfrage richtig an?
Steh da gerade voll aufm dem Schlauch.

Danke schon mal im Voraus.

Content-Key: 2730153038

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

Printed on: April 19, 2024 at 13:04 o'clock

Member: maretz
maretz May 09, 2022 updated at 11:29:50 (UTC)
Goto Top
select x.xyz, y.xyz from tabelle1 as x, tabelle 2 as y where x.id=y.id_produkt

beim "tabelle1 as x" bin ich mir nicht 100% sicher, aber das findet man wirklich in jedem mysql-beispiel...

ps. dann natürlich noch ein "and (x.id=123 or x.id=456 or x.id=134)" ranhängen
Member: derSESO
derSESO May 09, 2022 updated at 11:34:40 (UTC)
Goto Top
"tabelle1 as x" ist soweit korrekt. face-smile

Aber, mit der Abfrage bekomme auch die Produkte, wo nur eines der 3 Teile drin ist.
Ich brauche aber nur die Produkte, wo alle 3 Teile drin sind.
Member: gecekusu
gecekusu May 09, 2022 at 11:49:45 (UTC)
Goto Top
Zitat von @derSESO:

"tabelle1 as x" ist soweit korrekt. face-smile

Aber, mit der Abfrage bekomme auch die Produkte, wo nur eines der 3 Teile drin ist.
Ich brauche aber nur die Produkte, wo alle 3 Teile drin sind.

Wie wäre es mit ein AND statt OR für die Bediengungen?
Bin kein Experte aber ein Versuch schadet nichtface-smile
Member: derSESO
derSESO May 09, 2022 at 11:56:29 (UTC)
Goto Top
Schon versucht ... da findet er gar nix. face-smile

Hab es jetzt mal so gemacht ... zumindest stimmen die Ergebnisse - aber vlt. geht es effektiver.

SELECT DISTINCT P.id
FROM Produkt AS P
WHERE (SELECT id FROM Komponenten WHERE id_produkt = P.id AND nummer = '123') AND  
(SELECT id FROM Komponenten WHERE id_produkt = P.id AND nummer = '456') AND  
(SELECT id FROM Komponenten WHERE id_produkt = P.id AND nummer = '134')  
Member: gecekusu
gecekusu May 09, 2022 at 12:05:04 (UTC)
Goto Top
Zitat von @derSESO:

Schon versucht ... da findet er gar nix. face-smile

Hab es jetzt mal so gemacht ... zumindest stimmen die Ergebnisse - aber vlt. geht es effektiver.

SELECT DISTINCT P.id
FROM Produkt AS P
WHERE (SELECT id FROM Komponenten WHERE id_produkt = P.id AND nummer = '123') AND  
(SELECT id FROM Komponenten WHERE id_produkt = P.id AND nummer = '456') AND  
(SELECT id FROM Komponenten WHERE id_produkt = P.id AND nummer = '134')  

Habe wieder keine Ahnung aber einfach so gedacht und was geschrieben.

Wie wäre es mit

SELECT DISTINCT P.id
FROM Produkt AS P, Komponenten
WHERE id_produkt = P.id AND (p.nummer = 123 AND p.nummer = 456 AND p.nummer = 134)
Member: derSESO
derSESO May 09, 2022 at 12:09:19 (UTC)
Goto Top
Auch schon so versucht ... kein Ergebnis. face-smile
Member: akretschmer
akretschmer May 09, 2022 updated at 12:11:35 (UTC)
Goto Top
ich würde es so machen, aber ich habe kein MySQL ...


postgres=# select * from komponenten ;
 produkt | komponente 
---------+------------
       1 |        123
       1 |        456
       1 |        789
       1 |        134
       1 |        188
       2 |        123
       2 |        456
       2 |        677
       2 |        777
       2 |        223
       2 |        334
       3 |        123
       3 |        456
       3 |        781
       3 |        134
       3 |        333
       3 |        431
(17 rows)

postgres=# select produkt, array_agg(komponente) from komponenten group by produkt having array_agg(komponente) @> array[123,456,134]; ;
 produkt |         array_agg         
---------+---------------------------
       3 | {123,456,781,134,333,431}
       1 | {123,456,789,134,188}
(2 rows)

postgres=#
Member: gecekusu
gecekusu May 09, 2022 at 12:13:27 (UTC)
Goto Top
Oder man könnte es auch mit innerjoin versuchen aber wie man es genau macht, weiß ich nicht. Ich bin aber mir sicher, dass es mit innerjoin auch gehtface-smile
Member: gecekusu
gecekusu May 09, 2022 at 12:20:11 (UTC)
Goto Top
Schon wieder einen Vorschlag aber keine Ahnung ob es gehtface-smile

SELECT distinct P.id FROM Produkt

JOIN Komponenten

ON Komponenten.id_produkt = P.id

WHERE Komponenten.id_produkt = 123 AND Komponenten.id_produkt = 456 AND Komponenten.id_produkt = 134;
Member: akretschmer
akretschmer May 09, 2022 at 12:33:43 (UTC)
Goto Top
WHERE Komponenten.id_produkt = 123 AND Komponenten.id_produkt = 456 AND Komponenten.id_produkt = 134;


... wenn man das für 3Cent etwas weiter denkt, dann wäre 123 = 456 = 134. Merkste jetzt selber, oder?
Member: akretschmer
akretschmer May 09, 2022 at 12:56:57 (UTC)
Goto Top
Zitat von @akretschmer:

ich würde es so machen, aber ich habe kein MySQL ...

es gäbe noch weitere Wege, aber ich bin mir nicht einmal sicher, ob die Tabellenstruktur richtig normalisiert ist oder ob da Pfusch am Bau betrieben wurde...
Member: gecekusu
gecekusu May 09, 2022 at 13:11:12 (UTC)
Goto Top
Zitat von @akretschmer:

WHERE Komponenten.id_produkt = 123 AND Komponenten.id_produkt = 456 AND Komponenten.id_produkt = 134;


... wenn man das für 3Cent etwas weiter denkt, dann wäre 123 = 456 = 134. Merste jetzt selber, oder?

sieht so aus face-smile
Member: ukulele-7
ukulele-7 May 09, 2022 at 13:48:45 (UTC)
Goto Top
Ich hab das mal mit MSSQL gemacht:
WITH produkt(id,[name]) AS (
	SELECT 1,'EINS' UNION ALL  
	SELECT 2,'ZWEI' UNION ALL  
	SELECT 3,'DREI'  
	), komponenten(id,id_produkt,nummer) AS (
	SELECT 0,1,123 UNION ALL
	SELECT 1,1,456 UNION ALL
	SELECT 2,1,789 UNION ALL
	SELECT 3,1,134 UNION ALL
	SELECT 4,1,188 UNION ALL
	SELECT 5,2,123 UNION ALL
	SELECT 6,2,456 UNION ALL
	SELECT 7,2,677 UNION ALL
	SELECT 8,2,777 UNION ALL
	SELECT 9,2,223 UNION ALL
	SELECT 10,2,334 UNION ALL
	SELECT 11,3,123 UNION ALL
	SELECT 12,3,456 UNION ALL
	SELECT 13,3,781 UNION ALL
	SELECT 14,3,134 UNION ALL
	SELECT 15,3,333 UNION ALL
	SELECT 16,3,431
	), werteliste(nummer) AS (
	SELECT 123 UNION ALL
	SELECT 456 UNION ALL
	SELECT 134
	)
SELECT	k.id_produkt
FROM	werteliste w
INNER JOIN komponenten k
ON		w.nummer = k.nummer
GROUP BY k.id_produkt
HAVING count(*) = ( SELECT count(*) FROM werteliste )
Datentabellen habe ich jetzt im WITH-Teil, die können natürlich raus. Für die gesuchte Zusammensetzung habe ich eine Tabelle werteliste genommen, das kann natürlich auch ein Produkt aus der vorhandenen Tabelle sein.
Member: MadMax
MadMax May 09, 2022 at 18:48:27 (UTC)
Goto Top
Hallo derSESO,

grundsätzlich liegst Du mit Deiner Abfrage wohl richtig. Bei SQL Server würde es heißen:
SELECT P.id
FROM Produkt AS P
WHERE exists (SELECT id FROM Komponenten WHERE id_produkt = P.id AND nummer = '123') AND  
exists (SELECT id FROM Komponenten WHERE id_produkt = P.id AND nummer = '456') AND  
exists (SELECT id FROM Komponenten WHERE id_produkt = P.id AND nummer = '134')  

Ob es ein "exists" bei MySQL gibt, weiß ich nicht, aber so wie Du die Abfrage formuliert hast, scheint der Effekt derselbe zu sein, wenn Dein Ergebnis stimmt.

Alternative wäre noch:
SELECT DISTINCT P.id
FROM Produkt AS P
join Komponenten as k1 on k1.id_produkt = P.id AND k1.nummer = '123'  
join Komponenten as k2 on k2.id_produkt = P.id AND k2.nummer = '456'  
join Komponenten as k3 on k3.id_produkt = P.id AND k3.nummer = '134'  

Gruß, Mad Max
Member: akretschmer
Solution akretschmer May 09, 2022 at 18:56:12 (UTC)
Goto Top
Zitat von @MadMax:

Hallo derSESO,

grundsätzlich liegst Du mit Deiner Abfrage wohl richtig. Bei SQL Server würde es heißen:
SELECT P.id
FROM Produkt AS P
WHERE exists (SELECT id FROM Komponenten WHERE id_produkt = P.id AND nummer = '123') AND  
exists (SELECT id FROM Komponenten WHERE id_produkt = P.id AND nummer = '456') AND  
exists (SELECT id FROM Komponenten WHERE id_produkt = P.id AND nummer = '134')  

Ob es ein "exists" bei MySQL gibt, weiß ich nicht, aber so wie Du die Abfrage formuliert hast, scheint der Effekt derselbe zu sein, wenn Dein Ergebnis stimmt.

Alternative wäre noch:
SELECT DISTINCT P.id
FROM Produkt AS P
join Komponenten as k1 on k1.id_produkt = P.id AND k1.nummer = '123'  
join Komponenten as k2 on k2.id_produkt = P.id AND k2.nummer = '456'  
join Komponenten as k3 on k3.id_produkt = P.id AND k3.nummer = '134'  

Gruß, Mad Max

Beide Lösungen sind bei zunehmender Anzahl von zu prüfenden Komponenten zunehmend ###e. Stelle Dir mal die Anzahl im 4-stelligen Bereich vor: tausende EXISTS oder tausende JOIN. Pest oder Cholera?

Oder lieber, als weitere Variante zur schon gezeigten:

postgres=# select produkt, count(komponente) from komponenten where komponente in (123,456,134) group by produkt having count(produkt) = 3;
 produkt | count 
---------+-------
       3 |     3
       1 |     3
(2 rows)

Offensichtlich viel einfacher zu erweitern...
Member: MadMax
MadMax May 09, 2022 at 21:52:50 (UTC)
Goto Top
exists ist normalerweise recht schnell, vor allem in Verbindung mit einem Index. Da sehe ich das geringere Problem. Die joins sind natürlich so eine Sache ...

Dein erster Vorschlag hatte aber auch ein Problem: ob MySQL die Syntax von PostgreSQL beherrscht ist die Frage.

Dein letzter Vorschlag sieht aber sehr gut aus. Allerdings würde ich empfehlen, "count (distinct nummer)" zu verwenden. Jedenfalls, wenn kein eindeutiger Index auf id_produkt und nummer existiert und MySQL das beherrscht.

Gruß, Mad Max
Member: derSESO
derSESO May 10, 2022 updated at 05:06:49 (UTC)
Goto Top
So, Morgen zusammen.

Erstmal vielen Dank für die Unterstützung.

Kurze Info ... das EXISTS gibt es auch bei MySQL und funktioniert auch bei meinem aufwendigen SQL-Code.

Ich habe jetzt den Vorschlag von @akretschmer (ist als Lösung markiert) umgesetzt.
Funktioniert auch unter MySQL und der Zusammenbau des SQL-Code ist hier doch um einiges simpler als bei meiner Variante. Zumal so auch nur die Tabelle "Komponenten" verwendet wird.

--BED-- ist die Variable, in der die einzelnen Nummern zusammengesetzt sind ('123', '456', ...)
--BEDANZ-- ist die Variable, in der die Anzahl der einzelnen Nummern steht
SELECT id_produkt, COUNT(nummer)
FROM Komponenten
WHERE nummer in (--BED--)
GROUP BY id_produkt HAVING COUNT(id_produkt) = --BEDANZ--

Noch eine kurze Erklärung zum Sinn ...
Bei den Produkten handelt es sich um Computersysteme und das ganze soll den Kollegen das Finden von möglichen Systemen erleichtern, indem sich das Ergebnis mit jedem Hinzufügen einer Komponente immer weiter einschränkt.
Die Anzahl der Komponenten ist entsprechend auch einigermaßen beschränkt - es werden maximal 2-stellige Komponenten-Anzahlen geprüft.

Nochmals vielen Dank.