sql >> Base de Datos >  >> RDS >> Mysql

¿Cómo reemplazar cualquier otra instancia de un carácter particular en una cadena MySQL?

Debería considerar almacenar sus datos en un esquema normalizado. En su caso, la tabla debería verse así:

| id | k |        v |
|----|---|----------|
|  1 | A |       10 |
|  1 | B |       20 |
|  1 | C |       30 |
|  2 | A | Positive |
|  2 | B | Negative |

Este esquema es más flexible y verá por qué.

Entonces, ¿cómo convertir los datos dados en el nuevo esquema? Necesitará una tabla auxiliar que contenga números de secuencia. Dado que su columna es varchar(255) solo puede almacenar 128 valores (+ 127 delimitadores) en él. Pero vamos a crear 1000 números. Puede usar cualquier tabla con suficientes filas. Pero dado que cualquier servidor MySQL tiene information_schema.columns mesa, la usaré.

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 estos números como posición de los valores en su cadena uniendo las dos tablas.

Para extraer un valor de una cadena delimitada, puede usar substring_index() función. El valor en la posición i será

substring_index(substring_index(t.options, '|', i  ), '|', -1)

En su cadena tiene una secuencia de teclas seguida de sus valores. La posición de una tecla es un número impar. Entonces, si la posición de la tecla es i , la posición del valor correspondiente será i+1

Para obtener el número de delimitadores en la cadena y limitar nuestra combinación, podemos usar

char_length(t.options) - char_length(replace(t.options, '|', ''))

La consulta para almacenar los datos en forma normalizada sería:

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

Ahora ejecuta select * from normalized_table y obtendrás esto:

| id | k |        v |
|----|---|----------|
|  1 | A |       10 |
|  1 | B |       20 |
|  1 | C |       30 |
|  2 | A | Positive |
|  2 | B | Negative |

Entonces, ¿por qué este formato es una mejor opción? Además de muchas otras razones, una es que puede convertirlo fácilmente a su antiguo esquema 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 al formato deseado

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 |

Si no le importa la normalización y solo desea que se realice esta tarea, puede actualizar su tabla 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;

Y suelta la normalized_table .

Pero entonces no podrá usar consultas simples como

select *
from normalized_table
where k = 'A'

Ver demostración en rextester.com