Du bör överväga att lagra dina data i ett normaliserat schema. I ditt fall bör tabellen se ut så här:
| id | k | v |
|----|---|----------|
| 1 | A | 10 |
| 1 | B | 20 |
| 1 | C | 30 |
| 2 | A | Positive |
| 2 | B | Negative |
Detta schema är mer flexibelt och du kommer att se varför.
Så hur konverterar man givna data till det nya schemat? Du behöver en hjälptabell som innehåller sekvensnummer. Eftersom din kolumn är varchar(255)
du kan bara lagra 128 värden (+ 127 avgränsare) i den. Men låt oss bara skapa 1000 nummer. Du kan använda vilken tabell som helst med tillräckligt många rader. Men eftersom vilken MySQL-server som helst har information_schema.columns
tabell, jag kommer att använda det.
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;
Vi kommer att använda dessa siffror som position för värdena i din sträng genom att sammanfoga de två tabellerna.
För att extrahera ett värde från en avgränsad sträng kan du använda substring_index()
fungera. Värdet vid position i
kommer att vara
substring_index(substring_index(t.options, '|', i ), '|', -1)
I din sträng har du en sekvens av nycklar följt av dess värden. En nyckels position är ett udda tal. Så om nyckelns position är i
, kommer positionen för motsvarande värde att vara i+1
För att få antalet avgränsare i strängen och begränsa vår join kan vi använda
char_length(t.options) - char_length(replace(t.options, '|', ''))
Frågan för att lagra data i normaliserad form skulle vara:
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
Kör nu select * from normalized_table
och du får detta:
| id | k | v |
|----|---|----------|
| 1 | A | 10 |
| 1 | B | 20 |
| 1 | C | 30 |
| 2 | A | Positive |
| 2 | B | Negative |
Så varför är detta format ett bättre val? Förutom många andra skäl är en att du enkelt kan konvertera det till ditt gamla schema med
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 |
eller till önskat format
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 |
Om du inte bryr dig om normalisering och bara vill att den här uppgiften ska göras kan du uppdatera din tabell med
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;
Och släpp normalized_table
.
Men då kommer du inte att kunna använda enkla frågor som
select *
from normalized_table
where k = 'A'