Към съдържанието
wannacode

Миграции на схемата

Накратко: Миграцията е номерирана промяна на схемата на базата, която се пуска веднъж и държи базата на всеки компютър в една и съща версия.

Проблем

„no such column: phone“

Колега добавя колона phone към customers на своя компютър и пише кода, който я показва. На неговия компютър всичко работи. Новата версия отива на сайта и страницата „Клиенти“ пада с no such column: phone, защото в базата на сайта колона няма. На третия компютър в екипа пък има две колони, защото там някой я е добавил ръчно и с друго име.

Кодът е в git и всички имат една и съща версия. Схемата на базата трябва да е по същия начин: записана, с версии и еднаква навсякъде.

Идеята

Миграция: промяна с номер

Миграцията е малък SQL файл с номер. Всяка промяна по схемата е нов файл, и файловете са в git до кода:

migrations/
001_create_shop.sql
002_reviews.sql
003_customers_phone.sql

Базата помни кои са пуснати в таблица, обикновено schema_migrations. При старта (или при пускане на новата версия) инструментът пуска поред само файловете, които ги няма там. Тази база е на версия 2:

SQLбаза: магазин

Миграция 3 е промяната и записът за нея, заедно в една транзакция:

SQLбаза: магазин

Пуснеш ли я втори път в същото поле, ALTER TABLE ще спре с „duplicate column name“. Затова инструментът проверява schema_migrations, преди да пусне файла.

Виж го

Защо в транзакция

Ако миграцията спре по средата без транзакция, първата промяна остава, а записът за версията липсва. Следващият опит пак ще започне отначало и ще се спъне в собствената си първа стъпка. Тук втората заявка е грешка (същата колона два пъти):

SQLбаза: магазин

Транзакцията остава отворена с първия ALTER TABLE, а COMMIT не стига до базата. Пусни ROLLBACK; в полето и колоната phone изчезва: в SQLite и PostgreSQL промяната на схемата е част от транзакцията. Поправяш файла и го пускаш отново, а базата е точно каквато е била.

Пробвай

Миграция 3: точки за лоялност

Напиши миграция 3 за базата на версия 2: колона loyalty_points към customers (цяло число, задължително, по подразбиране 0) и запис (3, 'loyalty_points', '2026-10-25') в schema_migrations, в една транзакция.

SQLбаза: магазин

Идеята

Инструментите

Никой не пише проверката „коя миграция е пусната“ сам. Всеки език има инструмент, който пази таблицата с версиите и пуска файловете поред:

  • Java: Flyway или Liquibase (V3__customers_phone.sql);
  • C#: EF Core Migrations (dotnet ef migrations add CustomersPhone) пише миграцията от промяната в класовете;
  • Python: Alembic (към SQLAlchemy) или миграциите на Django;
  • JavaScript: Prisma Migrate, Knex или node-pg-migrate.

Някои пишат и обратна стъпка (down), която отменя миграцията. На сайт с истински данни връщането назад рядко е DROP COLUMN: ако новата колона вече пази данни, обратната стъпка ги губи. Затова „назад“ обикновено е нова миграция напред.

Виж го

Промяна на жива база: на стъпки

Сайтът не спира, докато новата версия се качва: известно време работят и старият, и новият код. Ако миграцията преименува city на town, старият код пада в същата секунда. Затова промяната е на три стъпки:

  1. Разширяване. Добави town и я попълни от city. Старият код не я забелязва.
  2. Преход. Новият код пише и в двете, а чете town. Изчакваш, докато старият код го няма никъде.
  3. Свиване. Отделна, по-късна миграция маха city.
SQLбаза: магазин

Упражнение

Име и фамилия поотделно

Магазинът иска да обръща клиентите по име („Здравей, Мария“). Направи първата стъпка (разширяване): добави колоните first_name и last_name към customers и ги попълни от name (всички имена са от две думи, разделени с интервал). Колоната name остава.

SQLбаза: магазин

Тест

Провери се

1. Миграция 5 вече е пусната на сайта. В нея има грешка в името на колона. Какво правиш?
2. Защо миграцията се пуска в транзакция?
3. Сайтът работи без спиране, а колоната city трябва да стане town. Кой е безопасният ред?

Създаден от Иво Гергов като част от кампанията на Evo Tech за достъпно обучение по програмиране ·