Below MySQL 8.0 the approach becomes much more complex..
The simple explainment about the hard part the first query
The subquery within
SELECT
GROUP_CONCAT(t.col1 ORDER BY col1 ASC) AS cvs
, COUNT(*) AS t_count
FROM
t
is making a comma separated values list.
Then the SQL number generator in combination with nested SUBSTRING_INDEX()
functions is splitting the comma separated values into records.
Which is then merged into one string by GROUP_CONCAT()
into the user variable basically iam generating dynamic SQL which lookes like '<value>' AS Column<number>[, ...]
that is what SELECT @aggregateSQLPart;
shows you
Query
SET @aggregateSQLPart = NULL;
# set max of GROUP_CONCAT higher as it defaults to 1024 bytes.
SET SESSION group_concat_max_len = @@max_allowed_packet;
SELECT
DISTINCT
GROUP_CONCAT(CONCAT("'",
SUBSTRING_INDEX(
SUBSTRING_INDEX(
t.cvs
, ','
, number_generator.number
)
, ','
, -1
) , "'" , " AS Column", number_generator.number
))
INTO @aggregateSQLPart
FROM (
SELECT
@row := @row + 1 AS number
FROM (
SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row1
CROSS JOIN (
SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row2
CROSS JOIN (
SELECT @row := 0
) init_user_params
) AS number_generator
CROSS JOIN (
SELECT
GROUP_CONCAT(t.col1 ORDER BY col1 ASC) AS cvs
, COUNT(*) AS t_count
FROM
t
) AS t
WHERE
number BETWEEN 1 AND t_count;
SELECT @aggregateSQLPart;
SET @SQL = CONCAT("
SELECT
"
, @aggregateSQLPart
);
SELECT @SQL;
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Result
| Column1 | Column2 | Column3 | Column4 | Column5 |
| ------- | ------- | ------- | ------- | ------- |
| A | B | C | D | E |
see demo
Note do not doubt about performance it runs in about a average off 5-10 ms in total on a "test" server.. Also note i select the user variables so you can see whats happing in between.