sql >> Databasteknik >  >> RDS >> Mysql

Hur ersätter man varannan instans av ett visst tecken i en MySQL-sträng?

Du bör överväga att lagra dina data i ett normaliserat schema. I ditt fall bör tabellen se ut så här:

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

Detta schema är mer flexibelt och du kommer att se varför.

Så hur konverterar man givna data till det nya schemat? Du behöver en hjälptabell som innehåller sekvensnummer. Eftersom din kolumn är varchar(255) du kan bara lagra 128 värden (+ 127 avgränsare) i den. Men låt oss bara skapa 1000 nummer. Du kan använda vilken tabell som helst med tillräckligt många rader. Men eftersom vilken MySQL-server som helst har information_schema.columns tabell, jag kommer att använda 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 kommer att använda dessa siffror som position för värdena i din sträng genom att sammanfoga de två tabellerna.

För att extrahera ett värde från en avgränsad sträng kan du använda substring_index() fungera. Värdet vid position i kommer att vara

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

I din sträng har du en sekvens av nycklar följt av dess värden. En nyckels position är ett udda tal. Så om nyckelns position är i , kommer positionen för motsvarande värde att vara i+1

För att få antalet avgränsare i strängen och begränsa vår join kan vi använda

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

Frågan för att lagra data i normaliserad form skulle vara:

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 och du får detta:

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

Så varför är detta format ett bättre val? Förutom många andra skäl är en att du enkelt kan konvertera det till ditt gamla schema 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 till önskat 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 |

Om du inte bryr dig om normalisering och bara vill att den här uppgiften ska göras kan du uppdatera din tabell 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;

Och släpp normalized_table .

Men då kommer du inte att kunna använda enkla frågor som

select *
from normalized_table
where k = 'A'

Se demo på rextester.com



  1. problem med att hitta en lista över filer i katalogen

  2. MYSQL-uppdatering med WHERE SELECT-underfrågans fel

  3. Använda subquery i en Check-sats i Oracle

  4. Server Side Events med PHP &MySQL