Você deve considerar armazenar seus dados em um esquema normalizado. No seu caso a tabela deve ficar assim:
| id | k | v |
|----|---|----------|
| 1 | A | 10 |
| 1 | B | 20 |
| 1 | C | 30 |
| 2 | A | Positive |
| 2 | B | Negative |
Este esquema é mais flexível e você verá o porquê.
Então, como converter os dados fornecidos no novo esquema? Você precisará de uma tabela auxiliar contendo números de sequência. Como sua coluna é
varchar(255)
você só pode armazenar 128 valores (+ 127 delimitadores) nele. Mas vamos apenas criar 1000 números. Você pode usar qualquer tabela com linhas suficientes. Mas como qualquer servidor MySQL tem o information_schema.columns
mesa, vou usá-la. drop table if exists helper_sequence;
create table helper_sequence (i int auto_increment primary key)
select null as i
from information_schema.columns c1
join information_schema.columns c2
limit 1000;
Usaremos esses números como posição dos valores em sua string juntando as duas tabelas.
Para extrair um valor de uma string delimitada você pode usar o
substring_index()
função. O valor na posição i
vai ser substring_index(substring_index(t.options, '|', i ), '|', -1)
Na sua string você tem uma sequência de chaves seguida de seus valores. A posição de uma chave é um número ímpar. Então, se a posição da chave for
i
, a posição do valor correspondente será i+1
Para obter o número de delimitadores na string e limitar nossa junção, podemos usar
char_length(t.options) - char_length(replace(t.options, '|', ''))
A consulta para armazenar os dados de forma normalizada seria:
create table normalized_table
select t.id
, substring_index(substring_index(t.options, '|', i ), '|', -1) as k
, substring_index(substring_index(t.options, '|', i+1), '|', -1) as v
from old_table t
join helper_sequence s
on s.i <= char_length(t.options) - char_length(replace(t.options, '|', ''))
where s.i % 2 = 1
Agora execute
select * from normalized_table
e você obterá isso:| id | k | v |
|----|---|----------|
| 1 | A | 10 |
| 1 | B | 20 |
| 1 | C | 30 |
| 2 | A | Positive |
| 2 | B | Negative |
Então, por que esse formato é a melhor escolha? Além de muitas outras razões, uma é que você pode facilmente convertê-lo para seu esquema antigo com
select id, group_concat(concat(k, '|', v) order by k separator '|') as options
from normalized_table
group by id;
| id | options |
|----|-----------------------|
| 1 | A|10|B|20|C|30 |
| 2 | A|Positive|B|Negative |
ou para o formato desejado
select id, group_concat(concat(k, '|', v) order by k separator ',') as options
from normalized_table
group by id;
| id | options |
|----|-----------------------|
| 1 | A|10,B|20,C|30 |
| 2 | A|Positive,B|Negative |
Se você não se importa com a normalização e só quer que essa tarefa seja feita, você pode atualizar sua tabela com
update old_table o
join (
select id, group_concat(concat(k, '|', v) order by k separator ',') as options
from normalized_table
group by id
) n using (id)
set o.options = n.options;
E solte a
normalized_table
. Mas você não poderá usar consultas simples como
select *
from normalized_table
where k = 'A'
Veja demonstração em rextester.com