MySQL разделяет многозначные строки из одной таблицы в другом столбце в новую таблицу

Я хочу разбить многозначные строки из одной таблицы в новую таблицу, состоящую из первичного ключа и результата разделенных строк.

Примеры строк:

table1.field1 (первичный ключ) = 100 , table1.field2 = 'abc,def,ghi'

В новой таблице (таблица 2) результат должен быть таким:

         **column1** **column2**
**row1**     100        'abc' 
**row2**     100        'def' 
**row3**     100        'ghi'
**row4**     etc         etc

Я знаю, как разделить таблицу1.поле2, но поскольку данные были такими большими, мне нужно автоматически вставить результат в таблицу2. Если я буду делать это вручную, это займет очень много времени. Кто-нибудь может мне помочь?


person user3698011    schedule 11.08.2017    source источник


Ответы (1)


Вот решение с использованием подготовленного оператора:

DROP TABLE IF EXISTS concatenatedVals;
CREATE TABLE concatenatedVals(`key` INTEGER UNSIGNED, concatenatedValue CHAR(255));

DROP TABLE IF EXISTS splitVals;
CREATE TABLE splitVals(`key` INTEGER UNSIGNED, splitValue CHAR(255));

INSERT INTO concatenatedVals VALUES (100, 'abc,def,ghi'), (200, 'jkl,mno,pqr');
SELECT * FROM concatenatedVals;

SET @VKey := '';
SET @VExec := (SELECT CONCAT('INSERT INTO splitVals VALUES', TRIM(TRAILING ',' FROM GROUP_CONCAT(CONCAT('(', @VKey:= `key`, ', \'', REPLACE(concatenatedValue, ',', CONCAT('\'), (', @VKey, ', \'')), '\'),') SEPARATOR '')), ';') FROM concatenatedVals);

PREPARE stmt FROM @VExec;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

SELECT * FROM splitVals;

Выходы:

SELECT * FROM splitVals;
+------+------------+
| key  | splitValue |
+------+------------+
|  100 | abc        |
|  100 | def        |
|  100 | ghi        |
|  200 | jkl        |
|  200 | mno        |
|  200 | pqr        |
+------+------------+
6 rows in set (0.00 sec)

Дайте знать, если у вас появятся вопросы.

Что касается вопроса, как я могу учитывать сценарии, в которых количество строк в моей исходной таблице означает, что подготовленный оператор превышает длину max-concat, см. следующий пример. Поскольку здесь используется цикл WHILE, он должен находиться внутри хранимой процедуры. Это можно адаптировать, чтобы разрешить имена таблиц и имена столбцов в качестве аргументов, используя дополнительные подготовленные операторы CONCATAND для динамического создания и выполнения команд. Однако сейчас измените имена таблиц и столбцов из моего примера, чтобы они соответствовали вашим данным, и все должно работать нормально.

DROP TABLE IF EXISTS concatenatedVals;
CREATE TABLE concatenatedVals(`key` INTEGER UNSIGNED, concatenatedValue CHAR(255));

DROP TABLE IF EXISTS splitVals;
CREATE TABLE splitVals(`key` INTEGER UNSIGNED, splitValue CHAR(255));

INSERT INTO concatenatedVals VALUES (100, 'abc,def,ghi'), (200, 'jkl,mno,pqr'),(300, 'abc,def,ghi'), (400, 'jkl,mno,pqr'),(500, 'abc,def,ghi'), (600, 'jkl,mno,pqr'),(700, 'abc,def,ghi'), (800, 'jkl,mno,pqr'),(900, 'abc,def,ghi'), (1000, 'jkl,mno,pqr');
SELECT * FROM concatenatedVals;

DELIMITER $

DROP PROCEDURE IF EXISTS loopStringSplit$

CREATE PROCEDURE loopStringSplit()
BEGIN

    DECLARE VKeyMaxLength, VConcatValMaxLength, VFixedCommandLength, VVariableCommandLength, VSelectLimit, VRowsToProcess, VRowsProcessed INT;

    SET VFixedCommandLength = CHAR_LENGTH(CONCAT('INSERT INTO splitVals VALUES;'));
    SET VKeyMaxLength = (SELECT MAX(CHAR_LENGTH(`key`)) FROM concatenatedVals);
    SET VConcatValMaxLength = (SELECT MAX(CHAR_LENGTH(concatenatedValue)) FROM concatenatedVals);
    SET VVariableCommandLength = CHAR_LENGTH('(,\'\')');

    SET VSelectLimit = FLOOR((@@group_concat_max_len - VFixedCommandLength) / (VKeyMaxLength + VConcatValMaxLength + VVariableCommandLength));

    SET VRowsToProcess := (SELECT COUNT(*) FROM concatenatedVals);
    SET VRowsProcessed = 0;

    SELECT VRowsProcessed, VRowsToProcess, VSelectLimit;
    WHILE VRowsProcessed < VRowsToProcess DO

        SET @VKey := '';
        SET @VExec := (SELECT CONCAT('INSERT INTO splitVals VALUES', TRIM(TRAILING ',' FROM GROUP_CONCAT(CONCAT('(', @VKey:= `key`, ', \'', REPLACE(concatenatedValue, ',', CONCAT('\'), (', @VKey, ', \'')), '\'),') SEPARATOR '')), ';') FROM (SELECT * FROM concatenatedVals LIMIT VRowsProcessed, VSelectLimit) A);

        SELECT @VExec;

        PREPARE stmt FROM @VExec;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;

        SET VRowsProcessed = VRowsProcessed + VSelectLimit;

        SELECT CONCAT('Processed rows: ', VRowsProcessed);

    END WHILE;

END$

DELIMITER ;

CALL loopStringSplit();

SELECT * FROM splitVals;

С уважением,

Джеймс

person James Scott    schedule 11.08.2017
comment
кстати, я попытался внедрить ваш код в большие данные и получил эту ошибку. Код предупреждения: 1260 Строка 14 была вырезана GROUP_CONCAT(), я пытаюсь использовать group_concat_max_len, но все еще не работает, у вас есть лучшее решение? спасибо раньше. - person user3698011; 11.08.2017
comment
Меня сейчас нет в офисе, сегодня вечером что-нибудь напишу и добавлю в ответ - person James Scott; 11.08.2017
comment
Было бы полезно узнать, с какими объемами данных вы имеете дело, если это возможно - это более 100000 строк? Если да, то в какой степени? Спасибо - person James Scott; 11.08.2017
comment
Спасибо, а сколько? Миллионы или миллиарды? Из-за ограничения длины переменной сеанса нам нужно использовать цикл, намного превышающий 10 ^ 7 строк, и было бы лучше смотреть за пределы чистого SQL, если только длительное время выполнения не приемлемо. - person James Scott; 12.08.2017
comment
ах, извините, что не дал четкого ответа. Мои данные около 500000 строк - person user3698011; 14.08.2017
comment
@ user3698011, см. обновленный ответ, приносим извинения за задержку. - person James Scott; 16.08.2017
comment
Все в порядке, Джеймс. Я следовал вашему коду и немного изменил его в своих данных, он работает! Большое спасибо 4 помощь. - person user3698011; 19.08.2017