Přidám poněkud delší a podrobnější vysvětlení kroků, které je třeba podniknout k vyřešení tohoto problému. Omlouvám se, pokud je to příliš dlouhé.
Začnu se základnou, kterou jste uvedli, a použiji ji k definování několika pojmů, které budu používat pro zbytek tohoto příspěvku. Toto bude základní tabulka :
select * from history;
+--------+----------+-----------+
| hostid | itemname | itemvalue |
+--------+----------+-----------+
| 1 | A | 10 |
| 1 | B | 3 |
| 2 | A | 9 |
| 2 | C | 40 |
+--------+----------+-----------+
To bude náš cíl, pěkná kontingenční tabulka :
select * from history_itemvalue_pivot;
+--------+------+------+------+
| hostid | A | B | C |
+--------+------+------+------+
| 1 | 10 | 3 | 0 |
| 2 | 9 | 0 | 40 |
+--------+------+------+------+
Hodnoty v souboru history.hostid
sloupec se změní na hodnoty y v kontingenční tabulce. Hodnoty v history.itemname
sloupec se změní na hodnoty x (ze zřejmých důvodů).
Když musím vyřešit problém s vytvořením kontingenční tabulky, řeším jej pomocí třífázového procesu (s volitelným čtvrtým krokem):
- vyberte požadované sloupce, tj. hodnoty y a hodnoty x
- rozšiřte základní tabulku o další sloupce – jeden pro každou hodnotu x
- seskupit a agregovat rozšířenou tabulku – jednu skupinu pro každou hodnotu y
- (volitelně) upravte agregovanou tabulku
Aplikujme tyto kroky na váš problém a uvidíme, co dostaneme:
Krok 1:Vyberte sloupce zájmu . V požadovaném výsledku hostid
poskytuje hodnoty y a itemname
poskytuje hodnoty x .
Krok 2:Rozšiřte základní tabulku o další sloupce . Obvykle potřebujeme jeden sloupec na hodnotu x. Připomeňme, že náš sloupec s hodnotou x je 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 |
+--------+----------+-----------+------+------+------+
Všimněte si, že jsme nezměnili počet řádků – pouze jsme přidali další sloupce. Všimněte si také vzoru NULL
s -- řádek s itemname = "A"
má pro nový sloupec A
nenulovou hodnotu a hodnoty null pro ostatní nové sloupce.
Krok 3:seskupte a agregujte rozšířenou tabulku . Musíme group by hostid
, protože poskytuje hodnoty y:
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 |
+--------+------+------+------+
(Upozorňujeme, že nyní máme jeden řádek na hodnotu y.) Dobře, už jsme skoro tam! Jen se musíme zbavit těch ošklivých NULL
s.
Krok 4:Zkrášlení . Všechny hodnoty null nahradíme nulami, aby se na výslednou sadu lépe dívalo:
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 |
+--------+------+------+------+
A máme hotovo – pomocí MySQL jsme vytvořili pěknou, hezkou kontingenční tabulku.
Aspekty při použití tohoto postupu:
- jakou hodnotu použít v dalších sloupcích. Použil jsem
itemvalue
v tomto příkladu - jakou "neutrální" hodnotu použít v dalších sloupcích. Použil jsem
NULL
, ale může to být také0
nebo""
, v závislosti na vaší přesné situaci - jakou agregační funkci použít při seskupování. Použil jsem
sum
, alecount
amax
se také často používají (max
se často používá při stavbě jednořadých „objektů“, které byly rozloženy do mnoha řad) - použití více sloupců pro hodnoty y. Toto řešení není omezeno na použití jednoho sloupce pro hodnoty y – stačí zapojit další sloupce do
group by
klauzuli (a nezapomeňteselect
je)
Známá omezení:
- toto řešení neumožňuje n sloupců v kontingenční tabulce – každý kontingenční sloupec je třeba při rozšiřování základní tabulky přidat ručně. Takže pro 5 nebo 10 hodnot x je toto řešení pěkné. Za 100, to není moc hezké. Existují některá řešení s uloženými procedurami generujícími dotaz, ale jsou ošklivá a je obtížné je správně nastavit. V současné době neznám dobrý způsob, jak tento problém vyřešit, když kontingenční tabulka potřebuje mít hodně sloupců.