Гость
Форумы / MySQL [игнор отключен] [закрыт для гостей] / PIVOT средствами MySQL / 3 сообщений из 3, страница 1 из 1
18.05.2018, 10:31
    #39646558
Akina
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PIVOT средствами MySQL
Поскольку надоело, решил написать процедуру, которая выполняет указанную в заголовке операцию. Предлагаю к тестированию.

Текст процедуры
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
DELIMITER @@;

DROP PROCEDURE IF EXISTS pivot@@;
CREATE PROCEDURE pivot ( IN schema_name VARCHAR(64) /* database name */
                       , IN table_name VARCHAR(64)  /* table name */
                       , IN id_name VARCHAR(64)     /* row values field name */
                       , IN key_name VARCHAR(64)    /* col values field name, 
                                                           must be char or varchar type 
                                                           and <= 64 chars long */
                       , IN value_name VARCHAR(64)  /* val values field name */
                       )
pivot:BEGIN
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET @error := 1;
    SET @error := 0;
    SELECT character_maximum_length 
        INTO @maxlen 
        FROM information_schema.columns
        WHERE table_schema = schema_name
          AND table_name = table_name
          AND column_name = key_name
          AND data_type IN ('char', 'varchar');
    IF @error OR !@maxlen OR @maxlen IS NULL THEN
        SELECT '@error OR @maxlen=0 OR @maxlen IS NULL', @error, @maxlen;
        LEAVE pivot;
    END IF;
    DROP TEMPORARY TABLE IF EXISTS temp_pivot;
    SET @sql := CONCAT('CREATE TEMPORARY TABLE temp_pivot (key_name VARCHAR(',
                       @maxlen,
                       ')) ENGINE=Memory SELECT DISTINCT `',
                       key_name,
                       '` key_name FROM `',
                       schema_name,
                       '`.`',
                       table_name,
                       '`;');
    PREPARE stmt FROM @sql;
    EXECUTE stmt;
    DROP PREPARE stmt;
    SELECT GROUP_CONCAT(CONCAT( ', MAX(CASE `',
                                key_name,
                                '` WHEN ''',
                                temp_pivot.key_name,
                                ''' THEN `',
                                value_name,
                                '` END) `',
                                temp_pivot.key_name,
                                '`') SEPARATOR '')
        INTO @sql
        FROM temp_pivot;
    DROP TEMPORARY TABLE temp_pivot;
    SET @sql := CONCAT('SELECT `',
                       id_name,
                       '`',
                       @sql,
                       ' FROM `',
                       schema_name,
                       '`.`',
                       table_name,
                       '` GROUP BY `',
					   id_name,
					   '`;');
    PREPARE stmt FROM @sql;
    EXECUTE stmt;
    DROP PREPARE stmt;
    SET @error := NULL;
    SET @maxlen := NULL;
    SET @sql := NULL;
    
END pivot@@;

DELIMITER ;


Тест/пример использования
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
DROP TABLE IF EXISTS testtab;
CREATE TABLE testtab(id INT, `key` VARCHAR(16), val INT);
INSERT INTO testtab (id, `key`, val)
VALUES (1,'key1',11),
       (1,'key2',12),
       (1,'key3',13),
       (2,'key1',21),
       (2,'key2',22),
       (2,'key4',24),
       (3,'key1',31),
       (3,'key2',32),
       (3,'key3',33),
       (3,'key4',34);
SELECT * FROM testtab;
       
CALL pivot('test', 'testtab', 'id', 'key', 'val');

DROP PROCEDURE pivot;
DROP TABLE testtab;

...
Рейтинг: 0 / 0
Период между сообщениями больше года.
10.02.2021, 14:28
    #40044015
Akina
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PIVOT средствами MySQL
Очередной раз достали вопросом. Решил использовать новые возможности. Код получился достаточно простым и лаконичным.

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
CREATE PROCEDURE pivot (tablename VARCHAR(64),
                        groupname VARCHAR(64),
                        pivotname VARCHAR(64),
                        valuename  VARCHAR(64))
BEGIN
SELECT CONCAT('CREATE VIEW to_columnslist AS\n',
              'SELECT DISTINCT CONCAT(\'`\', `', pivotname,'`, \'` VARCHAR(255) path \\\'$."\', ', pivotname,', \'"\\\'\') line\n',
              'FROM ', tablename)
INTO @sql;
SELECT @sql;
PREPARE stmt FROM @sql;
EXECUTE stmt;
DROP PREPARE stmt;
SELECT * FROM to_columnslist;
SELECT CONCAT(
'SELECT to_json.`', groupname,'`, parsed.*', '\n',
'FROM (SELECT `', groupname,'`, JSON_OBJECTAGG(`', pivotname,'`, `', valuename,'`) json_data', '\n',
'      FROM `', tablename,'`', '\n',
'      GROUP BY `', groupname,'`) to_json', '\n',
'CROSS JOIN JSON_TABLE( json_data,', '\n',
'                       "$" COLUMNS ( ', 
GROUP_CONCAT(line SEPARATOR ',\n                                     '),
' ) ) parsed'
) sql_text
INTO @sql
FROM to_columnslist;
SELECT @sql;
PREPARE stmt FROM @sql;
EXECUTE stmt;
DROP PREPARE stmt;
DROP VIEW to_columnslist;
END


Пример использования .

Само собой требуются валидные данные. В частности, пара (groupname, pivotname) должна быть уникальна. Если она неуникальна, и требуется некая агрегатная функция (скажем, суммирование), несложно на основании таблицы создать представление, приводящее данные к "нормальному" виду, и использовать его в качестве источника данных для процедуры.

Таблица должна быть в текущей БД (указание имени БД не предусмотрено), но это легко добавить, если нужно.
...
Рейтинг: 0 / 0
11.02.2021, 23:25
    #40044664
Akina
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PIVOT средствами MySQL
Дополнение.

Процедура может запросто сбойнуть по причине того, что по умолчанию значение @@group_concat_max_len всего 1024. Так что при большом количестве pivot-значений надо его увеличить.
...
Рейтинг: 0 / 0
Форумы / MySQL [игнор отключен] [закрыт для гостей] / PIVOT средствами MySQL / 3 сообщений из 3, страница 1 из 1
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]