#mysql #sql #unpivot
#mysql #sql #отключить
Вопрос:
Есть ли простой способ ВЫБРАТЬ одну строку в MySQL и перенести ее так, чтобы заголовки столбцов были перечислены в левом столбце, а значения перечислены в правом столбце?
Пример:
Вместо этого:
SELECT * FROM sock_table LIMIT 1;
--------- -------------- ------------
| sock_id | smell_factor | hole_count |
--------- -------------- ------------
| S000123 | 7.2 | 1 |
--------- -------------- ------------
… вы могли бы сделать что-то вроде этого:
SELECT TRANSPOSE(*) FROM sock_table LIMIT 1;
sock_id S000123
smell_factor 7.2
hole_count 1
Для тех, кто знаком с python — pandas, df.loc[0]
(где 0 — позиция индекса) автоматически выполняет эту функцию.
Это было бы для таблицы с десятками заголовков столбцов, поэтому ввод заголовков — это больше хлопот, чем того стоит.
Комментарии:
1. Использование
UNION
запросов для каждого столбца.2. Нет ничего встроенного, что делало бы это.
3. Просто обновил свой вопрос, чтобы отметить, что ввод имен столбцов приведет к поражению цели. И, возможно, @Barmar прав: отсутствие встроенной функциональности.
4. Если вы не хотите вводить все подзапросы для каждого столбца, вы можете создать хранимую процедуру, которая генерирует динамический SQL из
INFORMATION_SCHEMA.COLUMNS
таблицы.
Ответ №1:
В MYSQL используйте UNION ALL
для отмены:
(select 'stock_id' col, stock_id val FROM stock_table order by id limit 1)
union all (select 'smell_factor' col, smell_factor val FROM stock_table order by id limit 1)
union all (select 'hole_count' col, hole_count val FROM stock_table order by id limit 1)
Важные примечания:
-
для этого требуется один подзапрос на столбец
-
LIMIT
withoutORDER BY
нестабилен; вам нужно детерминированноеORDER BY
предложение — я предположил, что столбецid
можно использовать для упорядочения записей -
Типы данных значений должны быть согласованными — они могут вам понадобиться
cast
, если это не так
Редактировать
На самом деле, самые последние версии MySQL (8.0.19 или выше) позволяют выразить это с values row()
помощью and lateral
, что сокращает запрос:
select x.*
from (select * from mytable order by id limit 1) t,
lateral (values
row ('stock_id', t.stock_id),
row ('smell_factor', t.smell_factor),
row ('hole_count', t.hole_count)
) x(col, val)
Комментарии:
1. Спасибо за публикацию GMB, но эта примерная таблица в моем вопросе была упрощена, в ней будет значительно больше столбцов.
2. @elPastor: итак, вам нужно больше
union all
членов (по одному на столбец).3. Ишь, ок. Похоже, сок не стоит выжимать. В любом случае, еще раз спасибо.
4. @elPastor: альтернативный вариант см. В моей правке, если вы используете MySQL 8.0.14 или выше.
5. Спасибо, но опять же, мне пришлось бы вводить десятки столбцов, я ищу простую функцию, которой, как я предполагаю, не существует.
Ответ №2:
Предполагается sock_table
, что он состоит из этих трех столбцов и sock_id
является первичным ключом таблицы, чтобы использовать его в ORDER BY
предложении. Затем рассмотрите возможность использования этого оператора динамического запроса, чтобы получить непредоставленный результат :
SET @sql = NULL;
SELECT GROUP_CONCAT(
CONCAT(
'( SELECT "',column_name,'" AS col_name, ',column_name,' AS value
FROM ',table_name,' ORDER BY sock_id LIMIT 1 ) ' )
SEPARATOR 'UNION ALL ')
INTO @sql
FROM information_schema.columns c
WHERE table_name = 'sock_table'
ORDER BY ordinal_position;
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
В этом случае нет необходимости указывать имя каждого столбца по отдельности.