Debería considerar almacenar sus datos en un esquema normalizado. En su caso, la tabla debería verse así:
| id | k | v |
|----|---|----------|
| 1 | A | 10 |
| 1 | B | 20 |
| 1 | C | 30 |
| 2 | A | Positive |
| 2 | B | Negative |
Este esquema es más flexible y verá por qué.
Entonces, ¿cómo convertir los datos dados en el nuevo esquema? Necesitará una tabla auxiliar que contenga números de secuencia. Dado que su columna es varchar(255)
solo puede almacenar 128 valores (+ 127 delimitadores) en él. Pero vamos a crear 1000 números. Puede usar cualquier tabla con suficientes filas. Pero dado que cualquier servidor MySQL tiene information_schema.columns
mesa, la usaré.
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 estos números como posición de los valores en su cadena uniendo las dos tablas.
Para extraer un valor de una cadena delimitada, puede usar substring_index()
función. El valor en la posición i
será
substring_index(substring_index(t.options, '|', i ), '|', -1)
En su cadena tiene una secuencia de teclas seguida de sus valores. La posición de una tecla es un número impar. Entonces, si la posición de la tecla es i
, la posición del valor correspondiente será i+1
Para obtener el número de delimitadores en la cadena y limitar nuestra combinación, podemos usar
char_length(t.options) - char_length(replace(t.options, '|', ''))
La consulta para almacenar los datos en forma normalizada sería:
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
Ahora ejecuta select * from normalized_table
y obtendrás esto:
| id | k | v |
|----|---|----------|
| 1 | A | 10 |
| 1 | B | 20 |
| 1 | C | 30 |
| 2 | A | Positive |
| 2 | B | Negative |
Entonces, ¿por qué este formato es una mejor opción? Además de muchas otras razones, una es que puede convertirlo fácilmente a su antiguo esquema con
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 |
o al formato deseado
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 |
Si no le importa la normalización y solo desea que se realice esta tarea, puede actualizar su tabla con
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;
Y suelta la normalized_table
.
Pero entonces no podrá usar consultas simples como
select *
from normalized_table
where k = 'A'
Ver demostración en rextester.com