Měli byste zvážit uložení dat v normalizovaném schématu. Ve vašem případě by tabulka měla vypadat takto:
| id | k | v |
|----|---|----------|
| 1 | A | 10 |
| 1 | B | 20 |
| 1 | C | 30 |
| 2 | A | Positive |
| 2 | B | Negative |
Toto schéma je flexibilnější a uvidíte proč.
Jak tedy daná data převést do nového schématu? Budete potřebovat pomocnou tabulku obsahující pořadová čísla. Protože váš sloupec je varchar(255)
můžete do něj uložit pouze 128 hodnot (+ 127 oddělovačů). Ale pojďme vytvořit 1000 čísel. Můžete použít jakoukoli tabulku s dostatečným počtem řádků. Ale protože každý server MySQL má information_schema.columns
stůl, použiji ho.
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;
Tato čísla použijeme jako pozici hodnot ve vašem řetězci spojením dvou tabulek.
Chcete-li extrahovat hodnotu z řetězce s oddělovači, můžete použít substring_index()
funkce. Hodnota na pozici i
bude
substring_index(substring_index(t.options, '|', i ), '|', -1)
Ve vašem řetězci máte posloupnost klíčů následovanou jejich hodnotami. Pozice klíče je liché číslo. Pokud je tedy pozice klíče i
, pozice odpovídající hodnoty bude i+1
K získání počtu oddělovačů v řetězci a omezení našeho spojení můžeme použít
char_length(t.options) - char_length(replace(t.options, '|', ''))
Dotaz pro uložení dat v normalizované podobě by byl:
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
Nyní spusťte select * from normalized_table
a dostanete toto:
| id | k | v |
|----|---|----------|
| 1 | A | 10 |
| 1 | B | 20 |
| 1 | C | 30 |
| 2 | A | Positive |
| 2 | B | Negative |
Proč je tedy tento formát lepší volbou? Kromě mnoha dalších důvodů je jedním z důvodů, že jej můžete snadno převést na své staré schéma pomocí
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 |
nebo do požadovaného formátu
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 |
Pokud se nestaráte o normalizaci a chcete, aby byl tento úkol dokončen, můžete svou tabulku aktualizovat pomocí
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;
A vypusťte normalized_table
.
Pak ale nebudete moci používat jednoduché dotazy jako
select *
from normalized_table
where k = 'A'
Podívejte se na ukázku na rextester.com