sql >> Databáze >  >> RDS >> Mysql

MySQL – řádky do sloupců

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):

  1. vyberte požadované sloupce, tj. hodnoty y a hodnoty x
  2. rozšiřte základní tabulku o další sloupce – jeden pro každou hodnotu x
  3. seskupit a agregovat rozšířenou tabulku – jednu skupinu pro každou hodnotu y
  4. (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 , ale count a max 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ňte select 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ů.


  1. Seskupení podle klauzule v mySQL a postgreSQL, proč chyba v postgreSQL?

  2. Jak převést počet týdnů na datum?

  3. MySQL:@proměnná vs. proměnná. Jaký je v tom rozdíl?

  4. Jak přidat oddělovač do zřetězeného řetězce na serveru SQL – CONCAT_WS()