Как стать автором
Обновить

Комментарии 27

Хотелось бы и ложку дёгтя :)
В большой команде, если используется Flyway, приходится придумывать костыли в виде сообщения в чатике "я занял номер миграции 187", либо использовать флаг outOfOrder, и всё равно на ревью могут выскочить одинаковые номера и придется править.
У ликвибейза xml многословен, но у yaml и других вариантов нет автодополнения.

Для флайвея есть хак, который я подсмотрел в Play migrations — не указывайте описание в названии файла. Например, пишите V187__.sql. Тогда если кто-то занял версию, на этапе PR вы получите конфликт, который потом и исправите.

Или используйте SQL миграции в Liquibase!)

Спасибо за лайфхак!) Сейчас на проекте юзаю lb, но для себя выработал форматирование: каждый атрибут любого тёща, начиная со второго, с новой строки, чтобы преобразовать код из 2мерного в 1мерный по вертикали.
Да, часто видел, что пишут в нём только sql миграции, по-моему это ну такое, местами сомнительное. Почему тогда не flyway?)

Да, часто видел, что пишут в нём только sql миграции, по-моему это ну такое, местами сомнительное.

Чего же) если еще называть файлы как в Флайвее, так почти не отличить)

0001__create_table_test1.sql
--liquibase formatted sql
				
--changeset nvoxland:1
create table test1 (  
    id int primary key,
    name varchar(255)  
);  
--rollback drop table test1; 

Ещё один способ обойти ограничение — использовать дату вместо порядкового номера версии.
Например, V20210103_0530__Change_description.sql

Видел «решение» для большой команды в виде простого сервиса раздачи уникального растущего ключа. Пишется на чем угодно за вечер. Бот для чатика наверное еще пару вечеров может занять максимум.

outOfOrder жизненно необходимая вещь, когда в реп коммитят ежедневно 20+ человек.
Для MSSQL предпочитаю использовать связку:
1) отдельный SQL файл на каждый объект БД, где обязательно учитываются возможные проверки существования объектов
2) систему контроля версий (Git)
3) командный файл для наката изменений (BAT файл)
Подробно об этом написано в статье Ведение разработки БД. Шаблоны создания/изменения объектов MSSQL
Чем этот подход лучше инструментов в статье?
1) Не требуется изучать, устанавливать и настраивать сторонние компоненты (Liquibase, Flyway и др.). Все работает из коробки (с помощью sqlcmd и bcp).
2) Удобно отслеживать изменения объектов БД в Git, так как история одна (на каждый объект БД). В случае с liquibase, объект будет изменяться в каждой версии и поиск изменений усложняется.
3) При связке Liquibase и MSSQL обнаруживал баг, связанный с выводом комментариев через PRINT. Первый PRINT выводил нормально, а последующие не выводил. Наверняка, есть и другие баги.
Посмотрю внимательней на ваше решение, интересно.

А вот с багами Liquibase вообще засада. Год назад они очень медленно принимали патчи на гитхабе. Я фиксил парочку, и такая работа с комьюнити полностью отбила желание использовать Liquibase в будущем
Что будет, если в вашем решении скрипт упадет? Возможно ли поломать данные? Является ли выполнение скриптов идемпотентно?

И главное — как вставить миграции самих данных? Изменение структуры не так важно, как переливание данных из одних структур в другие
Цитата из статьи:
3) Если изменения касаются данных, то такие изменения ведутся в отдельном файле "CommonChanges (version 000).sql", который создается на каждое обновление БД
Все важные изменения в скрипте нужно выполнять с использованием транзакций.
А как обеспечивается правильный порядок создания объектов?
Простой пример из вашей статьи
-- Создать FOREIGN KEY, если его не существует
if not exists (select * from sys.foreign_keys where object_id = OBJECT_ID(N'dbo.FK_TableName_FieldName1') AND parent_object_id = OBJECT_ID(N'dbo.TableName'))
ALTER TABLE dbo.TableName WITH CHECK ADD CONSTRAINT FK_TableName_FieldName1 FOREIGN KEY(FieldName1)
REFERENCES dbo.ReferenceTableName (RefFieldName)
GO
IF EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'dbo.FK_TableName_FieldName1') AND parent_object_id = OBJECT_ID(N'dbo.TableName'))
ALTER TABLE dbo.TableName CHECK CONSTRAINT FK_TableName_FieldName1
GO

Нет проверки что существует dbo.ReferenceTableName, но как обеспечивается создание dbo.ReferenceTableName ДО dbo.TableName не написано :(

И как быть с взаимозависимыми таблицами? Когда есть условное table1.link --> table2.pk и table2.backlink -> table1.pk
FOREIGN KEY нужно создавать отдельными файлами. Перед созданием FOREIGN KEY, естественно, нужно выполнить создание таблицы.
Порядок выполнения задается в BAT'нике.
Я тоже пытался вести изменения по каждому объекту отдельно. Правда, я в итоге получил странные результы.
В Liquibase сделать то же что и у вас относительно легко — главный xml файл будет выступать в роли вашего bat файла.
В Flyway я агитировал за отдельный файл на сущность, и именовать файл как операця_сущность, вот тут есть пример.

Но я всё меньше вижу зачем нужен такой подход. DevOps чаще интересует что нужно накатить на прод базу во время деплоя. Желательно одним файлом. Разработчик же редко смотрит на историю объекта, интереснее история проекта в целом.
Я выступал за такой подход, поскольку хотел смердживать в Flyway миграции, если их станет слишком много и бежать они начнут слишком долго. Это редкая проблема, и единственные кто я знаю с ней столкнулись — ЛингуаЛео, когда у них перевалило кол-во миграций за несколько сотен. В микросервисном мире, или в модульных монолитах, вероятность такой задачи мала.
В Liquibase мне нужно было отслеживать историю объекта, потому что инструментом с самого начала не пользовались, а затем воспользовались криво. В итоге на чистой базе скрипты не работали.
Обе две эти задачи вылились в мой багфикс инструментов.
Это было 4 года назад. За это время никому вокруг меня трекать объекты было ненужно. Чаще нужен был единый файл изменений для ревью ДБА — фича которая есть в Л, и нету в бесплатном Ф.

Зачем вам нужен трекинг изменений пообъектно?
1) Если количество изменений очень большое, то ваш репозиторий будет сильно расти и файлов будет в разы больше
2) При возникновении бага, легко отследить в каком коммите была допущена ошибка (это ключевой пункт в выборе по объектного изменения)
3) Легко отслеживать новые объекты в репозитории
4) Я себя приучил к тому, чтобы делать один коммит, когда функционал завершен. Если функционал не завершен, не коммичу. Поэтому в истории Git можно быстро понять какие объекты изменились в одном коммите.

P.S.: В большом проекте, для удобства, я всегда веду отдельный текстовый файл с описанием всех изменений (что-то вроде WhatsNew). Это помогает при поиске проблем и дает другим участникам команды быстрее войти в проект и понять хронологию развития.
1) Количетво файлов целиком и полностью зависит от архитектуры приложения. Это должен быть хороший монолит с солидной историей, что бы наачть ощущать проблему описанную вами
2) Точно так же легко отследить и с инструментами — в лог попадает номер упавшей миграции, а в гите можно увидеть когда она была сделана
3) Непонятно кому это нужно. Ведь нужна хронология, о которой вы пишите в постскриптуме
4) Я стараюсь, что бы ПР был не больше 200 строчек, иначе его никто не ревьювит, а просто говорят — нормально всё сделано. В итоге фича превращается в несколько ПР-ов. Иногда я отступаю от этого правда, и создаю отдельную фича ветку, в которой может быть N коммитов, но каждый небольшой. Для моего случая это был бы один коммит в фича ветке. Фича ветка не сквошится и не фаст-форвардится, что бы тому, кто разбирается систорией было понятно где начало а где конец фичи.

Итого у вас два файла — батник с описанием как все объекты превращается в порядок миграций, и еще и текстовый файл. Не лучше бы вести один файл как в Ликьюбейсе, или зацепиться на семантику Флайвея?
Я ни в коем случае не принуждаю использовать мой подход. Я работаю так, как мне удобно и описал этот подход в статье. В Liquibase все описывается в XML формате, это можно рассматривать как плюс (если БД нужно поддерживать в нескольких СУБД) и как минусы:
1) Я не понимаю, зачем мне описывать XML в Liquibase, который будет непонятно как преобразовывать его в SQL, если сразу можно написать этот SQL запрос, вставить в него транзакции, предусмотреть все опции, хинты, проверки и т.д.
2) Отладка SQL запроса намного легче без использования сторонних библиотек
В Liquibase можно писать и обычные SQL файлы, жаль, это плохо отображено в документации
Попробуйте написать SQL запрос с использованием условия > (больше) или < (меньше) внутри XML.
В XML он начнет ругаться, что это системный символ и вам нужно будет при отладке заменять символы "& gt;" на > и "& lt;" на <. Разве это удобно?
Чтобы этого избежать, Вам нужно будет вести SQL файлы отдельно от XML.
 Вы опять говорите о xml формате, а я о sql. Liquibase спокойно относится к файлу, который будет выглядеть вот так:
--liquibase formatted sql
				
--changeset nvoxland:1
create table test1 (  
    id int primary key,
    name varchar(255)  
);  
--rollback drop table test1; 

В нём вы можете красиво описать всё что вам нужно. Если же использовать xml формат и тэг sql — то я с вами согласен, это полная лажа.
Подскажите пожалуйста можно ли в этих системах организовать:
— любые проверки до начала работы инкрементальных скриптов? Например: на наличие нужных расширений постгреса или на отсутствие активных сессий.
— действия, которые должны выполняться после каждого обновления?.. Например перекомпиляция пакетов (oracle), включение планировщиков.

И если можно, то как?
— любые проверки до начала работы инкрементальных скриптов? Например: на наличие нужных расширений постгреса или на отсутствие активных сессий.

В Liquibase можно с помощью фичи preconditions docs.liquibase.com/concepts/advanced/preconditions.html.

С точки зрения Flyway — это неправильно, и делать так нельзя. Но если очень хочется:
  • написать на sql, если это возможно
  • написать миграцию на джаве


— действия, которые должны выполняться после каждого обновления?.. Например перекомпиляция пакетов (oracle), включение планировщиков.

Repeatable migrations, есть в каждом туле.
В Ликьюбейсе еще есть runAlways.
В Флайвее callbacks. Документация слабо описивыает этот момент, но смысл в том, что вы создаете /sql/afterMigrate.sql файл, в котором описываете что должно выполняться, а имя файла указывает когда.

Справедливости ради, Java-имплементации миграций есть у обоих инструментов.

Я ввел в заблуждение, извините
Спасибо. Callbacks очень похоже на то, что решает указанные мною проблемы.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.