Přejmenování hodnot v MySQL ENUMu je operace, která může být zrádná. Mnoho vývojářů se pokouší o přímou změnu, což často vede ke ztrátě dat nebo chybám. Ukážeme si, jak na to správně a bezpečně.
Představme si typický scénář: Máte v databázi tabulku objednávek
(orders
) se sloupcem status
, který je typu ENUM.
Obsahuje hodnoty waiting_payment
, processing
,
shipped
a cancelled
. Požadavek je přejmenovat
waiting_payment
na unpaid
a shipped
na
completed
. Jak to udělat bez rizika?
Co nefunguje
Nejprve se podívejme na to, co nefunguje. Mnoho vývojářů zkusí tento přímočarý přístup:
-- TOHLE NEFUNGUJE!
ALTER TABLE orders
MODIFY COLUMN status ENUM(
'unpaid', -- původně 'waiting_payment'
'processing', -- beze změny
'completed', -- původně 'shipped'
'cancelled' -- beze změny
);
Takový přístup je receptem na katastrofu. MySQL se v takovém případě
pokusí mapovat existující hodnoty na nový ENUM, a protože původní hodnoty
už v definici nejsou, nahradí je prázdným řetězcem nebo vrátí chybu
Data truncated for column 'status' at row X
. V produkční
databázi by to znamenalo ztrátu důležitých dat.
Nejprve zálohujte!
Před jakoukoli změnou struktury databáze je naprosto klíčové vytvořit zálohu dat. Použijte MySQL-dump nebo jiný nástroj, kterému důvěřujete.
Správný postup
Správný postup se skládá ze tří kroků:
- Nejprve rozšíříme ENUM o nové hodnoty
- aktualizujeme data
- nakonec odstraníme staré hodnoty.
Pojďme si to ukázat:
1. Prvním krokem je přidání nových hodnot do ENUMu, zatímco ponecháme ty původní:
ALTER TABLE orders
MODIFY COLUMN status ENUM(
'waiting_payment', -- původní hodnota
'processing', -- zůstává stejná
'shipped', -- původní hodnota
'cancelled', -- zůstává stejná
'unpaid', -- nová hodnota (nahradí waiting_payment)
'completed' -- nová hodnota (nahradí shipped)
);
2. Nyní můžeme bezpečně aktualizovat existující data:
UPDATE orders SET status = 'unpaid' WHERE status = 'waiting_payment';
UPDATE orders SET status = 'completed' WHERE status = 'shipped';
3. A konečně, když jsou všechna data převedena na nové hodnoty, můžeme odstranit ty staré:
ALTER TABLE orders
MODIFY COLUMN status ENUM(
'unpaid',
'processing',
'completed',
'cancelled'
);
Proč tento postup funguje?
Je to díky tomu, jak MySQL pracuje s ENUM hodnotami. Když provádíme
ALTER TABLE s modifikací ENUMu, MySQL se snaží mapovat existující hodnoty
podle jejich textové podoby. Pokud původní hodnota v novém ENUMu
neexistuje, dojde v závislosti na nastavení sql_mode
buď
k chybě (při zapnutém STRICT_ALL_TABLES
) nebo k náhradě
prázdným řetězcem. Proto je klíčové mít v ENUMu vždy současně jak
staré, tak nové hodnoty.
V našem případě to znamená, že během přechodné fáze, kdy máme
v ENUMu hodnoty jako 'waiting_payment'
i 'unpaid'
,
každý záznam v databázi najde svůj přesný textový protějšek. Teprve
po UPDATE dotazech, kdy už víme, že všechna data používají nové hodnoty,
můžeme bezpečně odstranit ty staré.
Napište komentář