Jeg vil tilføje en noget længere og mere detaljeret forklaring af de trin, der skal tages for at løse dette problem. Jeg beklager, hvis den er for lang.
Jeg starter med den base, du har givet, og bruger den til at definere et par udtryk, som jeg vil bruge i resten af dette indlæg. Dette vil være basistabellen :
select * from history;
+--------+----------+-----------+
| hostid | itemname | itemvalue |
+--------+----------+-----------+
| 1 | A | 10 |
| 1 | B | 3 |
| 2 | A | 9 |
| 2 | C | 40 |
+--------+----------+-----------+
Dette vil være vores mål, den smukke pivottabel :
select * from history_itemvalue_pivot;
+--------+------+------+------+
| hostid | A | B | C |
+--------+------+------+------+
| 1 | 10 | 3 | 0 |
| 2 | 9 | 0 | 40 |
+--------+------+------+------+
Værdier i history.hostid
kolonne bliver y-værdier i pivottabellen. Værdier i history.itemname
kolonne bliver x-værdier (af indlysende årsager).
Når jeg skal løse problemet med at oprette en pivottabel, løser jeg det ved hjælp af en tre-trins proces (med et valgfrit fjerde trin):
- vælg kolonnerne af interesse, dvs. y-værdier og x-værdier
- udvid basistabellen med ekstra kolonner -- én for hver x-værdi
- grupper og aggregér den udvidede tabel -- én gruppe for hver y-værdi
- (valgfrit) forskønne den aggregerede tabel
Lad os anvende disse trin på dit problem og se, hvad vi får:
Trin 1:Vælg interessekolonner . I det ønskede resultat, hostid
giver y-værdierne og itemname
giver x-værdierne .
Trin 2:Udvid basistabellen med ekstra kolonner . Vi har typisk brug for én kolonne pr. x-værdi. Husk, at vores x-værdi kolonne er itemname
:
create view history_extended as (
select
history.*,
case when itemname = "A" then itemvalue end as A,
case when itemname = "B" then itemvalue end as B,
case when itemname = "C" then itemvalue end as C
from history
);
select * from history_extended;
+--------+----------+-----------+------+------+------+
| hostid | itemname | itemvalue | A | B | C |
+--------+----------+-----------+------+------+------+
| 1 | A | 10 | 10 | NULL | NULL |
| 1 | B | 3 | NULL | 3 | NULL |
| 2 | A | 9 | 9 | NULL | NULL |
| 2 | C | 40 | NULL | NULL | 40 |
+--------+----------+-----------+------+------+------+
Bemærk, at vi ikke ændrede antallet af rækker - vi tilføjede blot ekstra kolonner. Bemærk også mønsteret for NULL
s -- en række med itemname = "A"
har en ikke-nul værdi for ny kolonne A
, og null-værdier for de andre nye kolonner.
Trin 3:Grupper og saml den udvidede tabel . Vi skal group by hostid
, da det giver y-værdierne:
create view history_itemvalue_pivot as (
select
hostid,
sum(A) as A,
sum(B) as B,
sum(C) as C
from history_extended
group by hostid
);
select * from history_itemvalue_pivot;
+--------+------+------+------+
| hostid | A | B | C |
+--------+------+------+------+
| 1 | 10 | 3 | NULL |
| 2 | 9 | NULL | 40 |
+--------+------+------+------+
(Bemærk, at vi nu har én række pr. y-værdi.) Okay, vi er der næsten! Vi skal bare af med de grimme NULL
s.
Trin 4:forskønne . Vi vil bare erstatte alle nulværdier med nuller, så resultatsættet er pænere at se på:
create view history_itemvalue_pivot_pretty as (
select
hostid,
coalesce(A, 0) as A,
coalesce(B, 0) as B,
coalesce(C, 0) as C
from history_itemvalue_pivot
);
select * from history_itemvalue_pivot_pretty;
+--------+------+------+------+
| hostid | A | B | C |
+--------+------+------+------+
| 1 | 10 | 3 | 0 |
| 2 | 9 | 0 | 40 |
+--------+------+------+------+
Og vi er færdige -- vi har bygget en flot, smuk pivottabel ved hjælp af MySQL.
Overvejelser ved anvendelse af denne procedure:
- hvilken værdi der skal bruges i de ekstra kolonner. Jeg brugte
itemvalue
i dette eksempel - hvilken "neutral" værdi der skal bruges i de ekstra kolonner. Jeg brugte
NULL
, men det kan også være0
eller""
, afhængigt af din nøjagtige situation - hvilken aggregeringsfunktion der skal bruges ved gruppering. Jeg brugte
sum
, mencount
ogmax
bruges også ofte (max
bruges ofte, når man bygger "objekter" med én række, der var spredt ud over mange rækker) - brug af flere kolonner til y-værdier. Denne løsning er ikke begrænset til at bruge en enkelt kolonne til y-værdierne – sæt blot de ekstra kolonner ind i
group by
klausul (og glem ikke atselect
dem)
Kendte begrænsninger:
- denne løsning tillader ikke n kolonner i pivottabellen -- hver pivotkolonne skal tilføjes manuelt, når basistabellen udvides. Så for 5 eller 10 x-værdier er denne løsning fin. For 100, ikke så pænt. Der er nogle løsninger med lagrede procedurer, der genererer en forespørgsel, men de er grimme og svære at få rigtige. Jeg kender i øjeblikket ikke en god måde at løse dette problem på, når pivottabellen skal have mange kolonner.