Я пытался искать сообщения, но нашел решения только для SQL Server / Access. Мне нужно решение в MySQL (5.X).
У меня есть таблица (называемая историей) с 3 столбцами: hostid, itemname, itemvalue.
Если я сделаю 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 |
+--------+------+-----+-----+
Ответы:
Я собираюсь добавить несколько более длинное и подробное объяснение шагов, которые необходимо предпринять для решения этой проблемы. Прошу прощения, если это слишком долго.
Я начну с базы, которую вы дали, и воспользуюсь ею, чтобы определить пару терминов, которые я буду использовать в оставшейся части этого сообщения. Это будет базовая таблица :
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-значениями (по очевидным причинам).Когда мне нужно решить проблему создания сводной таблицы, я решаю ее, используя трехэтапный процесс (с необязательным четвертым шагом):
Давайте применим эти шаги к вашей проблеме и посмотрим, что мы получим:
Шаг 1: выберите интересующие столбцы . В желаемом результате
hostid
предоставляет значения y иitemname
предоставляет значения x .Шаг 2: расширьте базовую таблицу дополнительными столбцами . Обычно нам нужен один столбец на каждое значение x. Напомним, что наш столбец значений 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 | +--------+----------+-----------+------+------+------+
Обратите внимание, что мы не изменили количество строк - мы просто добавили дополнительные столбцы. Также обратите внимание на шаблон
NULL
s - строка сitemname = "A"
ненулевым значением для нового столбцаA
и нулевыми значениями для других новых столбцов.Шаг 3: сгруппируйте и объедините расширенную таблицу . Нам нужно
group by hostid
, поскольку он предоставляет значения 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 | +--------+------+------+------+
(Обратите внимание, что теперь у нас есть одна строка для каждого значения y.) Хорошо, мы почти у цели! Нам просто нужно избавиться от этих уродливых
NULL
.Шаг 4: украсить . Мы просто собираемся заменить любые нулевые значения нулями, чтобы на набор результатов было удобнее смотреть:
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
часто используется при создании одной строки «объекты» , которые были распространены во многих рядах)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;
источник
group by
.MAX
вместо,SUM
потому что моиitemValue
- это строки, а не числовые значения.Другой вариант, особенно полезный, если у вас есть много элементов, которые нужно развернуть, - это позволить 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
источник
'ifnull(SUM(case when itemname = ''',
с помощью''' then itemvalue end),0) AS
', `до'SUM(case when itemname = '''
с''' then itemvalue else 0 end) AS ',
. Это выводит такие термины, какSUM(case when itemname = 'A' then itemvalue else 0 end) AS 'A'
.Воспользовавшись идеей Мэтта Фенвика, которая помогла мне решить проблему (большое спасибо), давайте сведем ее к одному запросу:
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
источник
Я редактирую ответ Агунг Сагиты из подзапроса, чтобы присоединиться. Я не уверен, насколько велика разница между этими двумя способами, но просто для другой справки.
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
но это будет проблемой, если подзапрос даст больше, чем строку, используйте дополнительную агрегатную функцию в подзапросе
источник
Мое решение:
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
Это дает ожидаемые результаты в представленном случае.
источник
Я нашел один способ сделать мои отчеты, преобразующие строки в столбцы, почти динамическими, с помощью простых запросов. Вы можете увидеть и протестировать это онлайн здесь .
Количество столбцов запроса фиксировано, но значения являются динамическими и основаны на значениях строк. Вы можете его построить. Итак, я использую один запрос для создания заголовка таблицы, а другой - для просмотра значений:
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/ZSWKS28923
В качестве одного из реальных примеров использования этот отчет ниже показывает в столбцах часы отправления и прибытия лодки / автобуса с визуальным расписанием. Вы увидите один дополнительный столбец, который не используется в последнем столбце, чтобы не запутать визуализацию: ** Система продажи билетов для продажи билетов онлайн и предварительная
источник
Я сделаю это,
Group By hostId
тогда он покажет только первую строку со значениями,например:
источник
Если бы вы могли использовать 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
Подробнее здесь
источник
Это не точный ответ, который вы ищете, но это было решение, которое мне нужно в моем проекте, и надеюсь, что это кому-то поможет. Будет перечислено от 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
У этого кладбища два общих имени, поэтому имена перечислены в разных строках, связанных одним идентификатором, но двумя идентификаторами имен, и запрос дает что-то вроде этого
CemeteryID Cemetery_Name Latitude
1 Appleton, Sulpher Springs 35.4276242832293
источник
Мне жаль это говорить, и, возможно, я не решаю вашу проблему в точности, но PostgreSQL на 10 лет старше MySQL и чрезвычайно продвинут по сравнению с MySQL, и есть много способов легко этого добиться. Установите PostgreSQL и выполните этот запрос
CREATE EXTENSION tablefunc;
тогда вуаля! А вот обширная документация: PostgreSQL: Документация: 9.1: tablefunc или этот запрос
CREATE EXTENSION hstore;
потом снова вуаля! PostgreSQL: Документация: 9.0: hstore
источник