Dovresti considerare di archiviare i tuoi dati in uno schema normalizzato. Nel tuo caso la tabella dovrebbe assomigliare a:
| id | k | v |
|----|---|----------|
| 1 | A | 10 |
| 1 | B | 20 |
| 1 | C | 30 |
| 2 | A | Positive |
| 2 | B | Negative |
Questo schema è più flessibile e vedrai perché.
Quindi, come convertire i dati forniti nel nuovo schema? Avrai bisogno di una tabella di supporto contenente i numeri di sequenza. Poiché la tua colonna è varchar(255)
puoi memorizzare solo 128 valori (+ 127 delimitatori). Ma creiamo solo 1000 numeri. Puoi utilizzare qualsiasi tabella con un numero sufficiente di righe. Ma poiché qualsiasi server MySQL ha il information_schema.columns
tavolo, lo userò.
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;
Useremo questi numeri come posizione dei valori nella tua stringa unendo le due tabelle.
Per estrarre un valore da una stringa delimitata puoi usare substring_index()
funzione. Il valore nella posizione i
sarà
substring_index(substring_index(t.options, '|', i ), '|', -1)
Nella tua stringa hai una sequenza di chiavi seguita dai suoi valori. La posizione di una chiave è un numero dispari. Quindi se la posizione della chiave è i
, la posizione del valore corrispondente sarà i+1
Per ottenere il numero dei delimitatori nella stringa e limitare il nostro join possiamo usare
char_length(t.options) - char_length(replace(t.options, '|', ''))
La query per archiviare i dati in una forma normalizzata sarebbe:
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
Ora esegui select * from normalized_table
e otterrai questo:
| id | k | v |
|----|---|----------|
| 1 | A | 10 |
| 1 | B | 20 |
| 1 | C | 30 |
| 2 | A | Positive |
| 2 | B | Negative |
Allora perché questo formato è una scelta migliore? Oltre a molti altri motivi, uno è che puoi facilmente convertirlo nel tuo vecchio schema 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 nel formato desiderato
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 non ti interessa la normalizzazione e desideri solo che questa attività venga eseguita, puoi aggiornare la tua tabella 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;
E rilascia la normalized_table
.
Ma poi non sarai in grado di utilizzare query semplici come
select *
from normalized_table
where k = 'A'