Tabelle "pivotieren"

Hallo,
Ich habe heute eine Frage und hoffe mir kann hier jemand behilflich sein. ich bedanke mich schon im Voraus.
Ich habe folgende Tabelle:

id_kurs | mitarbeiter | orgnaisationseinheit | kursname | anzahl_stunden | studienjahr
1 | abc | gruppe1 | kurs1 | 10 | 2018
2 | def | gruppe2 | kurs1 | 11 | 2018
3 | ghi | gruppe2 | kurs2 | 9 | 2017
4 | jkl | gruppe3 | kurs3 | 8 | 2018

Ich möchte jetzt die Tabelle “drehen” sodass ich für jeden Kurs eine Zeile habe und dann für jeden Mitarbeiter eine Spalte mit den entsprechenden Stunden. Da die Mitarbeiter je nach Kurs dynamisch sind, also je Kurs immer unterschiedliche Mitarbeiter weiss ich nicht genau ob das überhaupt geht. Ich habe etwas mit crosstab() probiert, aber komme da irgendwie zu keinem Ergebnis. Hat hier vielleicht jemand eine Idee wie man so etwas umsetzen könnte und ob das überhaupt machbar ist?

LG
Hanno

test=*# select * from hanno ;
 id | mitarbeiter | org_einheit | kursname | stunden | jahr 
----+-------------+-------------+----------+---------+------
  1 | abc         | gruppe1     | kurs1    |      10 | 2018
  2 | def         | gruppe2     | kurs1    |      11 | 2018
  3 | ghi         | gruppe2     | kurs2    |       9 | 2017
  4 | jkl         | gruppe3     | kurs3    |       8 | 2018
(4 rows)

test=*# select kursname, sum(stunden) filter (where mitarbeiter = 'abc') as abc, sum(stunden) filter (where mitarbeiter = 'def') as def, sum(stunden) filter (where mitarbeiter = 'ghi') as ghi, sum(stunden) filter (where mitarbeiter = 'jkl') as jkl from hanno group by kursname order by kursname;
 kursname | abc | def | ghi | jkl 
----------+-----+-----+-----+-----
 kurs1    |  10 |  11 |     |    
 kurs2    |     |     |   9 |    
 kurs3    |     |     |     |   8
(3 rows)

test=*#

Du wirst das SQL dynamisch bauen müssen, in einer stored function, und mittels EXECUTE ausführen.


Alternativ: prüfe, ob Du das wirklich brauchst. Was ist das Ziel?

Das ist nicht wirklich machbar. Bei einer SQL Abfrage muss vor dem Ausführen der Abfrage klar sein, wie viele Spalten im Ergebnis vorhanden sind. Wenn Du vorher alle Mitarbeiter kennst, geht das mit der Lösung von akretschmer (weil Du vorher weißt wie viele Spalten das Ergebnis haben wird).

Aber ich vermute, dass Du die Anzahl der Mitarbeiter vor der Ausführung aber gar nicht wirklich kennst oder es zu viele Mitarbeiter sind, um das so umsetzen zu können.

Ein etwas hässlicher Workaround wäre die Mitarbeiter und deren Stunden in zwei Spalten als Arrays zurückzuliefern.

select id_kurs, 
       array_agg(mitarbeiter order by studienjahr) as mitarbeiter, 
       array_agg(anzahl_stunden order by studienjahr) as anzahl_stunden
from kurse
group by id_kurs
order by id_kurs;

Der erste Eintrag im Array für die Mitarbeiter gehört zum ersten Eintrag der Stunden, der zweite Mitarbeiter zu dem zweiten Eintrag der Stunden usw. Vielleicht willst Du das aber pro Jahr und Kurs haben, dann müsstest Du das GROUP BY entsprechend anpassen.

Aber wirklich schön ist das auch nicht.

Hallo,

Danke für die schnelle Antwort. Genau das habe ich mir auch schon gedacht. Der Grund warum ich das machen wollte/musste war das die Stunden bisher in einem Excel erfasst wurden. und dort halt pro Organisationseinheit ein eigenes Excel Sheet besteht. Im Excel Sheet war das halt aufgebaut das in den Spalten die Mitarbeiter stehen und in den Zeilen die Kurse. Dort sind dann die Mitarbeiter auch klar abgrenzbar, also immer diesselben. Um möglichst wenig Umgewöhnung zu haben sollte das also am besten wieder so aufgebaut sein. Jetzt sollen diese vielen Excel Sheets aber in einer DB zusammengeführt werden. Ab da sind es dann eben nicht mehr immer diesselben. Deshalb hatte ich das halt auch anders aufgebaut. Aber wie ihr schon richtig sagt ist es viel sinnvoller das so aufzubauen wie ich es auch schon gelöst hatte. Wollte nur sicher gehen, dass es “anders rum” sehr viel komplizierter und unschöner ist. Vielen Dank für die Inputs.

So am Rande es gibt auch die tablefunc-Extension, da gibt es die crosstab-Funktion. Allerdings muss auch da feststehen, was an Spalten vorkommen soll und so richtig schön ist das auch nicht. Allerdings gibt es auch dafür Procedures, die das dynamisieren können, hab das leiden noch nicht umgesetzt.

Gruß Uwe

Edit: Sorry habs überlesen. Man kann das auch mit dem JSON Datenformat machen, aber wie bisher, auch da will vorher festgelegt sein, was in welcher Reihenfolge erscheinen soll. Hab grad kein eingängiges Beispiel, also kurz umrissen, man aggregiert keys und values als arrays mit json_object zu einem JSON-Objekt, bei mir in einem Subselect. Auf das greift man dann mit lateral ( select * from json_to_record(json) as (Liste der keys und Formate) zu.