Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Como substituir todas as outras instâncias de um caractere específico em uma string MySQL?


Você deve considerar armazenar seus dados em um esquema normalizado. No seu caso a tabela deve ficar assim:
| id | k |        v |
|----|---|----------|
|  1 | A |       10 |
|  1 | B |       20 |
|  1 | C |       30 |
|  2 | A | Positive |
|  2 | B | Negative |

Este esquema é mais flexível e você verá o porquê.

Então, como converter os dados fornecidos no novo esquema? Você precisará de uma tabela auxiliar contendo números de sequência. Como sua coluna é varchar(255) você só pode armazenar 128 valores (+ 127 delimitadores) nele. Mas vamos apenas criar 1000 números. Você pode usar qualquer tabela com linhas suficientes. Mas como qualquer servidor MySQL tem o information_schema.columns mesa, vou usá-la.
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 esses números como posição dos valores em sua string juntando as duas tabelas.

Para extrair um valor de uma string delimitada você pode usar o substring_index() função. O valor na posição i vai ser
substring_index(substring_index(t.options, '|', i  ), '|', -1)

Na sua string você tem uma sequência de chaves seguida de seus valores. A posição de uma chave é um número ímpar. Então, se a posição da chave for i , a posição do valor correspondente será i+1

Para obter o número de delimitadores na string e limitar nossa junção, podemos usar
char_length(t.options) - char_length(replace(t.options, '|', ''))

A consulta para armazenar os dados de forma normalizada seria:
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

Agora execute select * from normalized_table e você obterá isso:
| id | k |        v |
|----|---|----------|
|  1 | A |       10 |
|  1 | B |       20 |
|  1 | C |       30 |
|  2 | A | Positive |
|  2 | B | Negative |

Então, por que esse formato é a melhor escolha? Além de muitas outras razões, uma é que você pode facilmente convertê-lo para seu esquema antigo com
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 |

ou para o formato desejado
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 você não se importa com a normalização e só quer que essa tarefa seja feita, você pode atualizar sua tabela com
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 solte a normalized_table .

Mas você não poderá usar consultas simples como
select *
from normalized_table
where k = 'A'

Veja demonstração em rextester.com