Mysql
 sql >> Database >  >> RDS >> Mysql

Come sostituire ogni altra istanza di un particolare carattere in una stringa MySQL?

Dovresti considerare di archiviare i tuoi dati in uno schema normalizzato. Nel tuo caso la tabella dovrebbe assomigliare a:

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

Questo schema è più flessibile e vedrai perché.

Quindi, come convertire i dati forniti nel nuovo schema? Avrai bisogno di una tabella di supporto contenente i numeri di sequenza. Poiché la tua colonna è varchar(255) puoi memorizzare solo 128 valori (+ 127 delimitatori). Ma creiamo solo 1000 numeri. Puoi utilizzare qualsiasi tabella con un numero sufficiente di righe. Ma poiché qualsiasi server MySQL ha il information_schema.columns tavolo, lo userò.

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;

Useremo questi numeri come posizione dei valori nella tua stringa unendo le due tabelle.

Per estrarre un valore da una stringa delimitata puoi usare substring_index() funzione. Il valore nella posizione i sarà

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

Nella tua stringa hai una sequenza di chiavi seguita dai suoi valori. La posizione di una chiave è un numero dispari. Quindi se la posizione della chiave è i , la posizione del valore corrispondente sarà i+1

Per ottenere il numero dei delimitatori nella stringa e limitare il nostro join possiamo usare

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

La query per archiviare i dati in una forma normalizzata sarebbe:

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

Ora esegui select * from normalized_table e otterrai questo:

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

Allora perché questo formato è una scelta migliore? Oltre a molti altri motivi, uno è che puoi facilmente convertirlo nel tuo vecchio schema 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 nel formato desiderato

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 non ti interessa la normalizzazione e desideri solo che questa attività venga eseguita, puoi aggiornare la tua tabella 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;

E rilascia la normalized_table .

Ma poi non sarai in grado di utilizzare query semplici come

select *
from normalized_table
where k = 'A'

Vedi demo su rextester.com