J'ai essayé de rechercher des publications, mais je n'ai trouvé que des solutions pour SQL Server/Access. J'ai besoin d'une solution dans MySQL (5.X).
J'ai une table (appelée history) avec 3 colonnes: hostid, itemname, itemvalue.
Si je fais un select (select * from history
), il retournera
+--------+----------+-----------+
| hostid | itemname | itemvalue |
+--------+----------+-----------+
| 1 | A | 10 |
+--------+----------+-----------+
| 1 | B | 3 |
+--------+----------+-----------+
| 2 | A | 9 |
+--------+----------+-----------+
| 2 | c | 40 |
+--------+----------+-----------+
Comment interroger la base de données pour retourner quelque chose comme
+--------+------+-----+-----+
| hostid | A | B | C |
+--------+------+-----+-----+
| 1 | 10 | 3 | 0 |
+--------+------+-----+-----+
| 2 | 9 | 0 | 40 |
+--------+------+-----+-----+
Je vais ajouter une explication un peu plus longue et détaillée des mesures à prendre pour résoudre ce problème. Je m'excuse si c'est trop long.
Je vais commencer par la base que vous avez fournie et l'utiliser pour définir quelques termes que je vais utiliser pour le reste de cet article. Ce sera le table de base:
select * from history;
+--------+----------+-----------+
| hostid | itemname | itemvalue |
+--------+----------+-----------+
| 1 | A | 10 |
| 1 | B | 3 |
| 2 | A | 9 |
| 2 | C | 40 |
+--------+----------+-----------+
Ce sera notre objectif, le joli tableau croisé dynamique:
select * from history_itemvalue_pivot;
+--------+------+------+------+
| hostid | A | B | C |
+--------+------+------+------+
| 1 | 10 | 3 | 0 |
| 2 | 9 | 0 | 40 |
+--------+------+------+------+
Les valeurs de la colonne history.hostid
deviendront valeurs y dans le tableau croisé dynamique. Les valeurs de la colonne history.itemname
deviendront valeurs x (pour des raisons évidentes).
Lorsque je dois résoudre le problème de la création d'un tableau croisé dynamique, je l'aborde en utilisant un processus en trois étapes (avec une quatrième étape facultative):
Appliquons ces étapes à votre problème et voyons ce que nous obtenons:
Étape 1: sélectionner les colonnes d'intérêt. Dans le résultat souhaité, hostid
fournit les valeurs y et itemname
fournit les valeurs x.
Étape 2: étendre la table de base avec des colonnes supplémentaires. Nous avons généralement besoin d'une colonne par valeur x. Rappelons que notre colonne x-value est 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 |
+--------+----------+-----------+------+------+------+
Notez que nous n'avons pas changé le nombre de lignes - nous avons simplement ajouté des colonnes supplémentaires. Notez également que le modèle NULL
s - une ligne contenant itemname = "A"
a une valeur non nulle pour la nouvelle colonne A
et des valeurs nulles pour les autres nouvelles colonnes.
Étape 3: regrouper et agréger la table étendue. Nous devons group by hostid
, car il fournit les valeurs 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 |
+--------+------+------+------+
(Notez que nous avons maintenant une ligne par valeur y.) D'accord, nous y sommes presque! Nous avons juste besoin de nous débarrasser de ces laids NULL
s.
étape 4: faire plaisir. Nous allons simplement remplacer les valeurs nulles par des zéros afin que l'ensemble de résultats soit plus agréable à regarder:
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 |
+--------+------+------+------+
Et nous avons terminé - nous avons construit un joli tableau croisé dynamique en utilisant MySQL.
Considérations lors de l'application de cette procédure:
itemvalue
dans cet exempleNULL
, mais il pourrait aussi s'agir de 0
ou ""
, selon votre situation exacte.sum
, mais count
et max
sont également souvent utilisés (max
est souvent utilisé lors de la création "d'objets" à une ligne répartis sur plusieurs lignes).group by
(et de ne pas oublier de select
les)Limites connues:
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;
Une autre option, particulièrement utile si vous avez plusieurs éléments à pivoter, est de laisser mysql créer la requête pour vous:
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 Ajout de quelques valeurs supplémentaires pour le voir fonctionner
GROUP_CONCAT
a une valeur par défaut de 1000. Si vous avez une très grosse requête, changez ce paramètre avant de l'exécuter.
SET SESSION group_concat_max_len = 1000000;
Tester:
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
Profitant de l'idée de Matt Fenwick qui m'a aidé à résoudre le problème (merci beaucoup), réduisons-la à une seule requête:
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
Je modifie la réponse de Agung Sagita de sous-requête à rejoindre. Je ne suis pas sûr de la différence entre cette façon, mais juste pour une autre référence.
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'
utiliser une sous-requête
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
mais ce sera un problème si une sous-requête résultant plus d'une ligne, utilisez une fonction d'agrégation supplémentaire dans la sous-requête
Je fais cela dans Group By hostId
alors il ne montrera que la première ligne avec des valeurs,
comme:
A B C
1 10
2 3
Ma solution:
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
Il produit les résultats attendus dans le cas soumis.
Je suis désolé de le dire et je ne résous peut-être pas votre problème exactement, mais PostgreSQL a 10 ans de plus que MySQL et est extrêmement avancé par rapport à MySQL et il existe de nombreuses façons de le résoudre facilement. Installez PostgreSQL et exécutez cette requête
CREATE EXTENSION tablefunc;
alors voila! Et voici une documentation complète: PostgreSQL: Documentation: 9.1: tablefunc ou cette requête
CREATE EXTENSION hstore;
là encore voila! PostgreSQL: Documentation: 9.0: hstore
Je trouve un moyen de rendre mes rapports de conversion de lignes en colonnes presque dynamiques à l'aide de requêtes simples. Vous pouvez voir et tester en ligne ici .
Le nombre de colonnes de requête est fixé mais les valeurs sont dynamiques et basé sur les valeurs des lignes. Vous pouvez le construire Donc, j'utilise une requête pour construire l'en-tête de la table et une autre pour voir les valeurs:
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;
Vous pouvez aussi le résumer:
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 |
+--------+------+------+------+
Résultats de RexTester :
http://rextester.com/ZSWKS2892
Pour un exemple réel d'utilisation, le présent rapport ci-dessous montre sous forme de colonnes les heures de départs des bateaux/bus avec un horaire visuel. Vous verrez une colonne supplémentaire non utilisée au dernier col sans confondre la visualisation: ** système de billetterie pour vendre des billets en ligne et présentiel
Si vous pouviez utiliser MariaDB , il existe une solution très très simple.
Depuis MariaDB-10.02 , un nouveau moteur de stockage appelé CONNECT peut nous aider à convertir les résultats de une autre requête ou table dans un tableau croisé dynamique, comme ce que vous voulez: Vous pouvez jeter un oeil à la documentation .
Tout d'abord installez le moteur de stockage connect .
Maintenant, la colonne pivot de notre table est itemname
et les données pour chaque élément sont situées dans la colonne itemvalue
. Vous pouvez ainsi obtenir le tableau croisé dynamique résultant à l'aide de cette requête:
create table pivot_table
engine=connect table_type=pivot tabname=history
option_list='PivotCol=itemname,FncCol=itemvalue';
Maintenant, nous pouvons sélectionner ce que nous voulons dans le pivot_table
:
select * from pivot_table
Ce n'est pas la réponse exacte que vous recherchez, mais c'était une solution dont j'avais besoin pour mon projet et j'espère que cela aidera quelqu'un. Ceci listera 1 à n éléments de ligne séparés par des virgules. Group_Concat rend cela possible dans 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
Ce cimetière a deux noms communs. Les noms sont donc répertoriés dans des rangées différentes connectées par un identifiant unique mais deux identifiants de noms. La requête produit un résultat similaire.
CemeteryID Cemetery_Name Latitude
1 Appleton, Sulpher Springs 35.4276242832293