246

投稿を検索しようとしましたが、SQL Server/Access のソリューションしか見つかりませんでした。MySQL (5.X) のソリューションが必要です。

hostid、itemname、itemvalue の 3 つの列を持つテーブル (履歴と呼ばれる) があります。
select(select * from history)を実行すると、返されます

   +--------+----------+-----------+
   | hostid | itemname | itemvalue |
   +--------+----------+-----------+
   |   1    |    A     |    10     |
   +--------+----------+-----------+
   |   1    |    B     |     3     |
   +--------+----------+-----------+
   |   2    |    A     |     9     |
   +--------+----------+-----------+
   |   2    |    c     |    40     |
   +--------+----------+-----------+

データベースにクエリを実行して次のようなものを返すにはどうすればよいですか

   +--------+------+-----+-----+
   | hostid |   A  |  B  |  C  |
   +--------+------+-----+-----+
   |   1    |  10  |  3  |  0  |
   +--------+------+-----+-----+
   |   2    |   9  |  0  |  40 |
   +--------+------+-----+-----+
4

13 に答える 13

325

この問題を解決するための手順について、もう少し長く詳細な説明を追加します。長すぎる場合は申し訳ありません。


いただいたベースから始めて、それを使用して、この投稿の残りの部分で使用するいくつかの用語を定義します。これがベース テーブルになります。

select * from history;

+--------+----------+-----------+
| hostid | itemname | itemvalue |
+--------+----------+-----------+
|      1 | A        |        10 |
|      1 | B        |         3 |
|      2 | A        |         9 |
|      2 | C        |        40 |
+--------+----------+-----------+

これが私たちの目標、きれいなピボットテーブルになります:

select * from history_itemvalue_pivot;

+--------+------+------+------+
| hostid | A    | B    | C    |
+--------+------+------+------+
|      1 |   10 |    3 |    0 |
|      2 |    9 |    0 |   40 |
+--------+------+------+------+

history.hostid列の値は、ピボット テーブルのy 値になります。history.itemname列の値はx 値になります(理由は明らかです)。


ピボット テーブルを作成する際の問題を解決する必要がある場合は、次の 3 段階のプロセス (オプションで 4 番目の手順を含む) を使用して取り組みます。

  1. 対象の列、つまりy 値x値を選択します
  2. 追加の列でベース テーブルを拡張します。x 値ごとに 1 つです。
  3. 拡張テーブルをグループ化して集計します -- 各y 値に対して 1 つのグループ
  4. (オプション) 集計テーブルを整形する

これらの手順を問題に適用して、結果を見てみましょう。

ステップ 1: 関心のある列を選択します。目的の結果でhostidは、y 値itemname提供し、x値を提供します。

ステップ 2: 追加の列でベース テーブルを拡張します。通常、x 値ごとに 1 つの列が必要です。x 値の列が であることを思い出してください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 |
+--------+----------+-----------+------+------+------+

行数は変更していないことに注意してください。列を追加しただけです。sのパターンにも注意してくださいNULL-- を持つ行itemname = "A"は、 new column に null 以外の値を持ちA、他の新しい列には null 値を持ちます。

ステップ 3: 拡張テーブルをグループ化して集約しますgroup by hostidy 値を提供するため、必要があります。

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 |
+--------+------+------+------+

(y 値ごとに 1 つの行があることに注意してください。) よし、あと少しです! これらの醜い を取り除く必要があるだけですNULL

ステップ 4: きれいにします。結果セットが見やすくなるように、null 値をゼロに置き換えるだけです。

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 |
+--------+------+------+------+

これで完了です。MySQL を使用して、見栄えの良いピボット テーブルを作成しました。


この手順を適用する際の考慮事項:

  • 追加の列で使用する値。itemvalueこの例で使用した
  • 追加の列で使用する「ニュートラル」値。私は を使用しましたが、正確な状況によってはまたはにNULLなることもあります0""
  • グループ化するときに使用する集計関数。私は を使用sumしましたがcountmaxもよく使用されます (max多くの行に分散された 1 行の「オブジェクト」を構築するときによく使用されます)。
  • y 値に複数の列を使用します。このソリューションは、y 値に 1 つの列を使用することに限定されません。余分な列をgroup by句に挿入するだけです (忘れないでselectください) 。

既知の制限:

  • このソリューションでは、ピボット テーブルに n 列を許可しません。ベース テーブルを拡張するときに、各ピボット列を手動で追加する必要があります。したがって、x 値が 5 または 10 の場合、このソリューションは適切です。100の場合、あまり良くありません。クエリを生成するストアド プロシージャを使用するソリューションがいくつかありますが、それらは見苦しく、正しく理解するのが困難です。現在、ピボット テーブルに多数の列が必要な場合に、この問題を解決する良い方法がわかりません。
于 2012-03-12T13:35:18.207 に答える
67
SELECT 
    hostid, 
    sum( if( itemname = 'A', itemvalue, 0 ) ) AS A,  
    sum( if( itemname = 'B', itemvalue, 0 ) ) AS B, 
    sum( if( itemname = 'C', itemvalue, 0 ) ) AS C 
FROM 
    bob 
GROUP BY 
    hostid;
于 2009-08-10T13:12:13.937 に答える
28

この問題を解決するのに役立った Matt Fenwick のアイデアを利用して (どうもありがとうございました)、クエリを 1 つだけに減らしてみましょう。

select
    history.*,
    coalesce(sum(case when itemname = "A" then itemvalue end), 0) as A,
    coalesce(sum(case when itemname = "B" then itemvalue end), 0) as B,
    coalesce(sum(case when itemname = "C" then itemvalue end), 0) as C
from history
group by hostid
于 2012-10-22T07:07:50.270 に答える
17

サブクエリから参加するAgung Sagitaの回答を編集します。この2つの方法の違いについてはわかりませんが、参考までに。

SELECT  hostid, T2.VALUE AS A, T3.VALUE AS B, T4.VALUE AS C
FROM TableTest AS T1
LEFT JOIN TableTest T2 ON T2.hostid=T1.hostid AND T2.ITEMNAME='A'
LEFT JOIN TableTest T3 ON T3.hostid=T1.hostid AND T3.ITEMNAME='B'
LEFT JOIN TableTest T4 ON T4.hostid=T1.hostid AND T4.ITEMNAME='C'
于 2012-12-19T07:22:31.527 に答える
13

サブクエリを使用する

SELECT  hostid, 
    (SELECT VALUE FROM TableTest WHERE ITEMNAME='A' AND hostid = t1.hostid) AS A,
    (SELECT VALUE FROM TableTest WHERE ITEMNAME='B' AND hostid = t1.hostid) AS B,
    (SELECT VALUE FROM TableTest WHERE ITEMNAME='C' AND hostid = t1.hostid) AS C
FROM TableTest AS T1
GROUP BY hostid

ただし、サブクエリが行を超える場合は問題になります。サブクエリでさらに集計関数を使用します

于 2011-11-02T06:00:53.437 に答える
5

私の解決策:

select h.hostid, sum(ifnull(h.A,0)) as A, sum(ifnull(h.B,0)) as B, sum(ifnull(h.C,0)) as  C from (
select
hostid,
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 
) h group by hostid

提出されたケースで期待される結果が得られます。

于 2014-01-29T21:06:37.287 に答える
4

これを作成すると、次の ようなGroup By hostId値を持つ最初の行のみが表示されます。

A   B  C
1  10
2      3
于 2011-04-28T08:54:56.810 に答える
1

これはあなたが探している正確な答えではありませんが、私のプロジェクトで必要な解決策であり、これが誰かに役立つことを願っています. これにより、カンマで区切られた 1 から n 行の項目がリストされます。Group_Concat は MySQL でこれを可能にします。

select
cemetery.cemetery_id as "Cemetery_ID",
GROUP_CONCAT(distinct(names.name)) as "Cemetery_Name",
cemetery.latitude as Latitude,
cemetery.longitude as Longitude,
c.Contact_Info,
d.Direction_Type,
d.Directions

    from cemetery
    left join cemetery_names on cemetery.cemetery_id = cemetery_names.cemetery_id 
    left join names on cemetery_names.name_id = names.name_id 
    left join cemetery_contact on cemetery.cemetery_id = cemetery_contact.cemetery_id 

    left join 
    (
        select 
            cemetery_contact.cemetery_id as cID,
            group_concat(contacts.name, char(32), phone.number) as Contact_Info

                from cemetery_contact
                left join contacts on cemetery_contact.contact_id = contacts.contact_id 
                left join phone on cemetery_contact.contact_id = phone.contact_id 

            group by cID
    )
    as c on c.cID = cemetery.cemetery_id


    left join
    (
        select 
            cemetery_id as dID, 
            group_concat(direction_type.direction_type) as Direction_Type,
            group_concat(directions.value , char(13), char(9)) as Directions

                from directions
                left join direction_type on directions.type = direction_type.direction_type_id

            group by dID


    )
    as d on d.dID  = cemetery.cemetery_id

group by Cemetery_ID

この墓地には 2 つの一般的な名前があるため、名前は 1 つの ID で接続された異なる行にリストされますが、2 つの名前 ID があり、クエリは次のようなものを生成します

    CemeteryID Cemetery_Name Latitude
    1 Appleton,Sulpher Springs 35.4276242832293

于 2013-10-10T14:58:42.663 に答える