Du bør overveje at gemme dine data i et normaliseret skema. I dit tilfælde skulle tabellen se sådan ud:
| id | k | v |
|----|---|----------|
| 1 | A | 10 |
| 1 | B | 20 |
| 1 | C | 30 |
| 2 | A | Positive |
| 2 | B | Negative |
Dette skema er mere fleksibelt, og du vil se hvorfor.
Så hvordan konverteres de givne data til det nye skema? Du skal bruge en hjælpetabel med sekvensnumre. Da din kolonne er varchar(255)
du kan kun gemme 128 værdier (+ 127 skilletegn) i den. Men lad os bare skabe 1000 numre. Du kan bruge enhver tabel med nok rækker. Men da enhver MySQL-server har information_schema.columns
tabel, vil jeg bruge 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 vil bruge disse tal som placering af værdierne i din streng ved at forbinde de to tabeller.
For at udtrække en værdi fra en afgrænset streng kan du bruge substring_index()
fungere. Værdien ved position i
vil være
substring_index(substring_index(t.options, '|', i ), '|', -1)
I din streng har du en sekvens af taster efterfulgt af dens værdier. En nøgles position er et ulige tal. Så hvis nøglens position er i
, vil positionen for den tilsvarende værdi være i+1
For at få antallet af skilletegn i strengen og begrænse vores join, kan vi bruge
char_length(t.options) - char_length(replace(t.options, '|', ''))
Forespørgslen om at gemme dataene i en normaliseret form ville være:
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
og du får dette:
| id | k | v |
|----|---|----------|
| 1 | A | 10 |
| 1 | B | 20 |
| 1 | C | 30 |
| 2 | A | Positive |
| 2 | B | Negative |
Så hvorfor er dette format et bedre valg? Udover mange andre grunde er den ene, at du nemt kan konvertere det til dit gamle skema 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 til dit ønskede 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 |
Hvis du er ligeglad med normalisering og bare vil have denne opgave udført, kan du opdatere din tabel 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;
Og slip normalized_table
.
Men så vil du ikke være i stand til at bruge simple forespørgsler som
select *
from normalized_table
where k = 'A'