MySQL Pivot-Zeile in dynamische Anzahl von Spalten

81

Nehmen wir an, ich habe drei verschiedene MySQL-Tabellen:

Tabelle products:

id | name
 1   Product A
 2   Product B

Tabelle partners:

id | name
 1   Partner A
 2   Partner B

Tabelle sales:

partners_id | products_id
          1             2
          2             5
          1             5
          1             3
          1             4
          1             5
          2             2
          2             4
          2             3
          1             1

Ich möchte eine Tabelle mit Partnern in den Zeilen und Produkten als Spalten erhalten. Bisher konnte ich eine Ausgabe wie folgt erhalten:

name      | name      | COUNT( * )
Partner A   Product A          1
Partner A   Product B          1
Partner A   Product C          1
Partner A   Product D          1
Partner A   Product E          2
Partner B   Product B          1
Partner B   Product C          1
Partner B   Product D          1
Partner B   Product E          1

Verwenden dieser Abfrage:

SELECT partners.name, products.name, COUNT( * ) 
FROM sales
JOIN products ON sales.products_id = products.id
JOIN partners ON sales.partners_id = partners.id
GROUP BY sales.partners_id, sales.products_id
LIMIT 0 , 30

aber ich hätte stattdessen gerne etwas wie:

partner_name | Product A | Product B | Product C | Product D | Product E
Partner A              1           1           1           1           2
Partner B              0           1           1           1           1

Das Problem ist, dass ich nicht sagen kann, wie viele Produkte ich haben werde, sodass sich die Spaltennummer abhängig von den Zeilen in der Produkttabelle dynamisch ändern muss.

Diese sehr gute Antwort scheint mit MySQL nicht zu funktionieren: T-SQL Pivot? Möglichkeit zum Erstellen von Tabellenspalten aus Zeilenwerten

FeeJai
quelle
Weitere Vorschläge finden Sie unter Link Zeile zu Spalte .
Bhavin Pokiya
Dynamischer Erbauer
Rick James
@BhavinPokiya, ein von Ihnen bereitgestellter MS SQL-Server-Link, der als MySQL gekennzeichnet ist.
MattSom

Antworten:

107

Leider hat MySQL keine PIVOTFunktion, die im Grunde das ist, was Sie versuchen zu tun. Sie müssen also eine Aggregatfunktion mit einer CASEAnweisung verwenden:

select pt.partner_name,
  count(case when pd.product_name = 'Product A' THEN 1 END) ProductA,
  count(case when pd.product_name = 'Product B' THEN 1 END) ProductB,
  count(case when pd.product_name = 'Product C' THEN 1 END) ProductC,
  count(case when pd.product_name = 'Product D' THEN 1 END) ProductD,
  count(case when pd.product_name = 'Product E' THEN 1 END) ProductE
from partners pt
left join sales s
  on pt.part_id = s.partner_id
left join products pd
  on s.product_id = pd.prod_id
group by pt.partner_name

Siehe SQL-Demo

Da Sie die Produkte nicht kennen, möchten Sie dies wahrscheinlich dynamisch durchführen. Dies kann mit vorbereiteten Anweisungen erfolgen.

Bei dynamischen Pivot-Tabellen (Zeilen in Spalten umwandeln) würde Ihr Code folgendermaßen aussehen:

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'count(case when Product_Name = ''',
      Product_Name,
      ''' then 1 end) AS ',
      replace(Product_Name, ' ', '')
    )
  ) INTO @sql
from products;

SET @sql = CONCAT('SELECT pt.partner_name, ', @sql, ' from partners pt
left join sales s
  on pt.part_id = s.partner_id
left join products pd
  on s.product_id = pd.prod_id
group by pt.partner_name');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

Siehe SQL-Demo

Es ist wahrscheinlich erwähnenswert, dass dies GROUP_CONCATstandardmäßig auf 1024 Bytes beschränkt ist. Sie können dies umgehen, indem Sie es für die Dauer Ihres Verfahrens höher einstellen, d. H.SET @@group_concat_max_len = 32000;

Taryn
quelle
2
Der Kommentar am Ende zu "max_len" war ein Lebensretter! Danke für den Tipp.
Edward
1
Wenn Sie nach dynamischeren Pivot-Abfragen suchen, überprüfen Sie dies bitte: boynux.com/creating-pivot-reports-in-mysql
Boynux
Was passiert, wenn der Produktname lautetProductA') from partners pt; truncate partners;
avatarofhope2
@ avatarofhope2 Ist das eine Frage oder eine Implikation? Wenn dies einen Einspritzwinkel ergibt, wie ist der richtige Umgang damit?
MattSom