投稿を検索しようとしましたが、SQL Server/Accessのソリューションしか見つかりませんでした。 MySQL(5.X)のソリューションが必要です。
Hostid、itemname、itemvalueの3つの列を持つ表(履歴と呼ばれる)があります。
選択(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 |
+--------+------+-----+-----+
この問題を解決するための手順について、もう少し長く詳細な説明を追加します。長すぎるとおIびします。
あなたが与えたベースから始め、このポストの残りの部分で使用するいくつかの用語を定義するために使用します。これはベーステーブルになります。
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-valuesになります。 history.itemname
列の値はx-valuesになります(明らかな理由により)。
ピボットテーブルを作成する問題を解決する必要がある場合は、3つのステップのプロセス(オプションの4番目のステップ)を使用してそれに取り組みます。
これらの手順を問題に適用して、得られるものを見てみましょう。
ステップ1:対象の列を選択する。目的の結果では、hostid
はy-valuesを提供し、itemname
はx-valuesを提供します。
ステップ2:余分な列でベーステーブルを拡張する。通常、x値ごとに1つの列が必要です。 x-value列は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 |
+--------+----------+-----------+------+------+------+
行数は変更していないことに注意してください。列を追加しただけです。また、NULL
sのパターンに注意してください。itemname = "A"
のある行は、新しい列A
にNULL以外の値を持ち、他の新しい列にNULL値を持ちます。
ステップ3:拡張テーブルをグループ化して集約する。 y値を提供するため、group by hostid
が必要です。
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
sを取り除く必要があります。
ステップ4:prettify。ヌル値をゼロに置き換えるだけなので、結果セットは見やすくなります。
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
を使用しましたが、count
とmax
もよく使用されます(max
は、多くの行に分散された1行の「オブジェクト」を構築するときによく使用されます)group by
句にプラグインするだけです(select
を忘れないでください)既知の制限:
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;
ピボットする必要のあるアイテムが多数ある場合に特に便利な別のオプションは、mysqlにクエリを作成させることです。
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'ifnull(SUM(case when itemname = ''',
itemname,
''' then itemvalue end),0) AS `',
itemname, '`'
)
) INTO @sql
FROM
history;
SET @sql = CONCAT('SELECT hostid, ', @sql, '
FROM history
GROUP BY hostid');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
FIDDLE 動作を確認するためにいくつかの追加値を追加しました
GROUP_CONCAT
のデフォルト値は1000なので、非常に大きなクエリがある場合は、実行前にこのパラメーターを変更してください
SET SESSION group_concat_max_len = 1000000;
テスト:
DROP TABLE IF EXISTS history;
CREATE TABLE history
(hostid INT,
itemname VARCHAR(5),
itemvalue INT);
INSERT INTO history VALUES(1,'A',10),(1,'B',3),(2,'A',9),
(2,'C',40),(2,'D',5),
(3,'A',14),(3,'B',67),(3,'D',8);
hostid A B C D
1 10 3 0 0
2 9 0 40 5
3 14 67 0 8
問題を解決するのに役立った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
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'
サブクエリを使用する
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
しかし、サブクエリが行よりも多い場合は問題になります。サブクエリでさらに集計関数を使用します
これをGroup By hostId
にすると、値を持つ最初の行のみが表示されます。
like:
A B C
1 10
2 3
私の解決策:
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
提出されたケースで期待される結果を生成します。
単純なクエリを使用して、レポートを行から列にほぼ動的に変換する1つの方法を見つけました。確認してテストできます オンラインはこちら 。
クエリの列の数は固定ですが、値は動的であり、行の値に基づきます。それを構築することができますので、テーブルヘッダーを構築するために1つのクエリを使用し、値を表示するために別のクエリを使用します。
SELECT distinct concat('<th>',itemname,'</th>') as column_name_table_header FROM history order by 1;
SELECT
hostid
,(case when itemname = (select distinct itemname from history a order by 1 limit 0,1) then itemvalue else '' end) as col1
,(case when itemname = (select distinct itemname from history a order by 1 limit 1,1) then itemvalue else '' end) as col2
,(case when itemname = (select distinct itemname from history a order by 1 limit 2,1) then itemvalue else '' end) as col3
,(case when itemname = (select distinct itemname from history a order by 1 limit 3,1) then itemvalue else '' end) as col4
FROM history order by 1;
要約することもできます:
SELECT
hostid
,sum(case when itemname = (select distinct itemname from history a order by 1 limit 0,1) then itemvalue end) as A
,sum(case when itemname = (select distinct itemname from history a order by 1 limit 1,1) then itemvalue end) as B
,sum(case when itemname = (select distinct itemname from history a order by 1 limit 2,1) then itemvalue end) as C
FROM history group by hostid order by 1;
+--------+------+------+------+
| hostid | A | B | C |
+--------+------+------+------+
| 1 | 10 | 3 | NULL |
| 2 | 9 | NULL | 40 |
+--------+------+------+------+
RexTester の結果:
http://rextester.com/ZSWKS2892
実際の使用例の1つとして、このレポートは、船/バスの出発時間を視覚的なスケジュールで列に示しています。視覚化を混乱させることなく、最後の列で使用されていない追加の列が1つ表示されます。 **オンラインおよびプレゼンシャルでチケットを販売するための発券システム
申し訳ありませんが、あなたの問題を正確に解決していないかもしれませんが、PostgreSQLはMySQLより10年古く、MySQLに比べて非常に高度であり、これを簡単に実現する方法はたくさんあります。 PostgreSQLをインストールし、このクエリを実行します
CREATE EXTENSION tablefunc;
出来上がり!そして、ここに広範なドキュメントがあります: PostgreSQL:Documentation:9.1:tablefunc またはこのクエリ
CREATE EXTENSION hstore;
その後、再び出来上がり! PostgreSQL:ドキュメント:9.0:hstore
これはあなたが探している正確な答えではありませんが、私のプロジェクトで必要なソリューションであり、これが誰かの助けになることを願っています。これにより、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つの共通名があるため、名前は単一のIDで接続された異なる行にリストされますが、2つの名前IDとクエリは次のようなものを生成します
CemeteryID Cemetery_Name Latitude
1 Appleton、Sulpher Springs 35.4276242832293
MariaDB を使用できる場合、非常に簡単な解決策があります。
MariaDB-10.02以降、 CONNECT と呼ばれる新しいストレージエンジンが追加されました。別のクエリまたはテーブルをピボットテーブルに追加します。これは、必要なものと同じです。 ドキュメント をご覧ください。
まず最初に 接続ストレージエンジンをインストール 。
これで、テーブルのピボット列はitemname
になり、各アイテムのデータはitemvalue
列にあるため、次のクエリを使用して結果ピボットテーブルを作成できます。
create table pivot_table
engine=connect table_type=pivot tabname=history
option_list='PivotCol=itemname,FncCol=itemvalue';
これで、pivot_table
から必要なものを選択できます。
select * from pivot_table