Pull to refresh

Comments 46

Не этот ли проект был недавно обсуждаем на РСДН?
Ruby, какие еще мнения :)

С одной стороны — более удобно, чем SQL/DDL, с другой — еще один DSL учить. Ну ни данные, ни объекты типа хранимок, констрейнтов и триггеров насколько я понимаю, мигрировать он не умеет. Или я что-то пропустил?
Верно, ничего этого пока не умеет. Но научу.
UFO just landed and posted this here
Не сильно вижу удобства пользования. Лично мне подобная тузла видится как генератор описаний структуры баз + аналог diff, но завязанный на структуру этих описаний. И дальше — обычный svn/любая другая VCS.

Насколько я понимаю, именно diff-функционала у вас и нет :)

А так я могу и ALTER-код писать, зато в родном, привычном SQL. Зачем мне учить ваш язык?
Первые два предложения недопонял. Diff-а действительно нет, да он и не нужен: вы, похоже, не с той стороны смотрите. Дифф — это вы, и именно вы описываете отличие N-й версии схемы от N+1-й.

Что касается SQL DDL, то проблемы вот в чем: вам придется писать скрипты как для применения изменений, так и для их отмены. Кроме того, вручную, в тех же скриптах, проверять версию схемы чтобы ненароком не применить то, чего применять не следует. Да и ручное отслеживание версий (а-ля insert into TableVersion (Table, Version) values ('Foo', 23)) сильно захламляет код.
Исчо раз формулирую суть своих мыслей:

1. Я при разработке не хочу задумываться о том, что у меня и где изменится. Я просто меняю и пишу код. Потом уже, на стадии сбора и коммита изменений — автоматика разбирается что где изменилось и что с этим делать, в спорных случаях — предлагает вам вручную резолвить конфликты.
Вы же предлагаете подход, когда я задумываюсь об изменениях и пишу эти изменения — проецируя этот подход на код программы — можете представить, что вместо написания кода вы пишете diff'ы. Не кажется ли вам это несколько абсурдным?

2. Отмена изменений нужна очень редко и в основном внедренцам. Для этого проще использовать бекапы и не заморачиваться. Для разработчиков же в принципе возникает лишь задача получить нужную ревизию (кода/базы), что легко решается созданием новой БД с накатом всех изменений вплоть до нужной ревизии. Тогда мы храним лишь изначальные CREATE'ы и кучу ревизионных ALTER'ов к ним. Даже тулзу отката в принципе можно таким образом сделать — анализировать все ALTER'ы между заданными ревизиями и генерить отменяющие ALTER'ы.
1. Если бы я писал диффы для кода — то да, это было бы посильнее «Фауста» Гёте. В случае с кодом мы просто записываем новое «состояние» (поскольку код по своей натуре stateless), а дифф создается автоматически и этот процесс детерминированный.

В случае с БД мы просто обязаны описывать переходы между версиями. Это требует много дополнительной работы. Чаще всего требуется несколько итераций, и каждая итерация требует ручного написания кода — а это очевидные накладные расходы и работа, которую в принципе можно не делать. В случае с Wizardby это будет выглядеть так: написал миграцию, выполнил «wizardby u», попробовал; не понравилось — «wizardby ro» и пишем миграцию заново. Понравилось — коммитимся.

2. Отмена изменений чаще всего нужна именно при разработке — выше я уже описал, как именно это может пригодиться.
Опять же — состояние БД жёстко завязано на состояние кода, соответственно ревизия кода = ревизии базы. Новую ревизию кода я могу писать не день и не два, и частично изменённый код нужен в разработке вместе с частично изменённой БД. Я пишу дифф на БД, другой программист пишет дифф на БД, я пишу ещё пять диффов к БД, потом выясняется, что я пошёл не тем путём и всё надо отменить. Как предлагается совмещать это с диффами другого программиста? Я же могу накатывать/откатывать только линейно. А если часть моего первого диффа на БД уже используется другим программистом?
Вообще говоря, это замечание имеет смысл и при «классической» схеме работы со схемой БД — с написанными врукопашную DDL-скриптами будут те же проблемы. А вообще, вот тут рассматривается простой пример совместной работы.

В итоге, по-моему, проблемы получаются идентичные. И все из-за того, что БД — она stateful и с этим надо считаться.
Проблемы идентичные, а зачем тогда новый язык? :)

Это я всё к тому, что нужен инструмент «снятия» состояния базы в текстовое описание (те же CREATE и т.д.) и ещё один инструмент для получения диффов из таких состояний. Стандартный diff тут не подойдёт, ибо на выходе надо генерить ALTER'ы. И тогда, на мой взгляд, всё будет просто, прозрачно и понятно :)
Ну ежели на то пошло, то вполне можно было бы ограничится одним-единственным Тюринг-полным языком — остановиться на каком-нибудь Лиспе и успокоиться.

Это, конечно, демагогия. Практически все новые средства в IT создаются с простой целью: повышение уровня абстракции, чтобы меньше времени и ресурсов тратить на бесполезные с точки зрения каждой конкретной проблемы действия (например, управление памятью в случае, когда не пишутся драйвера устройств и подобное низкоуровневое ПО) и сосредотачиваться на самом вопросе.

Вот собственно это средство и было создано с такой целью — пусть ненамного, но повысить уровень абстракции (к тому же, это всего лишь альфа-версия).

Что касается диффов схемы, то ситуация следующая: Visual Studio Team System 2008 Database Edition (традиционное для Microsoft название) пошла практически по этому пути. Но чистые диффы не получились даже у них: средство рефакторинга записывает последовательность действий, и потом воспроизводит их на целевой БД (это необходимо для, например, переименований). Но эта телега и стоит совершенно запредельно, и работает только с SQL Server'ом.
Если уж говорить о наращивании уровней абстракции, тогда уж лучше снимать все данные со слоя объектно-реляционного маппинга.

Вы не подумайте, я не против вашего решения, я просто до сих пор не могу представить себе ситуации, где его можно удобно применить, и при этом бы не существовало более удобных решений. Т.е. не могу ответить на вопрос «зачем?».
ORMы с диффами (на их счет, я надеюсь, мы пришли к консенсусу, что автоматические диффы для БД — практически утопия), насколько я знаю, дружат плохо.

А «зачем?»… Лично мне удобнее, в Рельсах подобное есть давно и разработчики не жалуются. То есть, жалуются, конечно, но в основном на то, что тамошние миграции слишком обобщенные — у DHH, к примеру, свой особый взгляд на ссылочную целостность и поэтому Foreign Key'и там не в фаворе.
В общем, я просто для себя понял, что лично мне оно бесполезно :)
Похожая утилита миграции оказалась очень удобной (я бы сказал, незаменимой), когда возникла необходимость обновлять десятки инсталляций разных версий системы с разными наборами плагинов на разных БД.
Автоматика справляется с генерацией скриптов вообще ужасно, потому что вместе с изменениями структуры таблиц нужно еще аккуратно (и максимально быстро) мигрировать сами данные.
да, но вам же фактически пришлось самому написать скрипт миграции. Аналогично можно было бы написать соответствующий DDL-код…
Да, приходится писать аналоги DDL-скриптов, но движок миграции решает кучу проблем. Как минимум, генерит правильный DDL для конкретной БД (а их использовалось штук 5 разных). Еще приятная фича — автоматические проверки позволяют корректно продолжить скрипт миграции, если при предыдущем запуске что-то помешало ему отработать до конца.
О, вот это уже преимущество. Хотя, многие субд позволяют и DDL выполнять в транзакции…
Да, но только не Oracle :(
Фигассе! Там правда этого нельзя?
Угу, каждая DDL-операция в Oracle выполняется в отдельной транзакции, независимо от желания пользователя.
UFO just landed and posted this here
В смысле, завернуть внутрь транзакции? Не помогает: Oracle принудительно коммитит текущую транзакцию перед выполнением DDL.
UFO just landed and posted this here
Макс, и оставайся при нем.
Каждую ддл, можно завернуть в транзакцию.
А выше молодому человеку, я бы посоветовал воздержаться, если не в курсе.
UFO just landed and posted this here
Для подобных целей использую «dbForge Studio for MySQL»:
www.devart.com/ru/dbforge/mysql/studio/

Программа отлично сравнивает базы данных и генерирует скрипт для «обновления». Сравнения могут быть как по структуре (таблицы, процедуры, триггеры), так и по самим данным из таблиц. Русская версия бесплатная для частного использования.

Сама же разработка и обновления ведутся с использованием «инкрементальных» обновлений. На сервере находится рабочая база, а также тестовая — для разработок. Как только приходит время, с помощью dbForge генерируется скрипт обновления рабочей базы до «тестовой» (т.е. новой версии). Сам скрипт сохраняется в отдельный файл вида «Обновление с версии 10 до 11.sql». Затем проверяется, для какой и до какой версии базы необходимо провести обновления — и вручную текст из соответствующих скриптов копируется в один файл для установки (получается что-то вроде «Обновление с версии 9 до 12.sql»).
Программа отлично сравнивает базы данных и генерирует скрипт для «обновления»
Как она справляется с переименованиями?
и вручную текст из соответствующих скриптов копируется в один файл для установки
Залог успешной разработки ПО: сборка и развертывание должны производиться без ручного вмешательства, желательного нажатием красной кнопки в чемоданчике.
Переименования? Никак. Программа не может определить, что именно эту колонку или таблицу переименовали в другую. Максимум что можно сделать — после анализа различий исключить соответствующие объекты для генерации кода и вручную отредактировать получившийся скрипт.
Софтина хороша, но вот те кому посчасливилось иметь VS Team Suite может использовать Database Project GDR который войдёт в состав VS 2010, но в рамках программы лояльности MSFT выпустило обновление к VS 2008 но только для редакций Team Suite & Database Edition.

Microsoft® Visual Studio Team System 2008 Database Edition GDR
Видел его. Всё, что есть подобного для .NET, уже посмотрел. «Его писали не они», отчасти. Но в основном — слишком длительный цикл работы получается, нет той интерактивности. Пока в Студии класс добавишь, напишешь, скомпилируешь — сбивает с мысли.
Мы используем на проекте довольно успешно. Часто приходится добавлять кастомную логику для миграции, например изменить формат поля и т.п. Не представляю как это сделать с Wizardby. Также бесплатно получаем intellisense, знакомый синтаксис, возможность запуска миграций прямо в коде (из юнит-тестов, например)
Что есть «формат поля»? Если тип, то alter column Foo type => Int64, nullable => true. Дополнительную логику можно реализовывать с помощью дополнительного же SQL'я, но тогда теряется платформонезависимость.
Если эта логика пишется на C#, то ничего не теряется. Но сделать что-то вроде FullName = FirstName + " " + LastName приходится довольно часто и это самое простое, что может понадобиться.
Поддержка Oracle планируется? Когда? =)
Oracle будет точно. В релизной первой версии будет.
liquibase.org — не без недостатков софтина, но из всего что я видел (за исключением migrate-а на RoR) — самая стабильная.
Это Java (что, в принципе, терпимо), да еще и XML, от которого у меня в глазах рябит, используемый как язык программирования (а вот этого я терпеть не намерен).
Меня вот всегда интересовало в таких утилитах: а как делать миграции, сложнее добавления колонки/таблицы? Например объединение/разделение таблиц с переносом данных. Даже переименование столбца или изменение типа его данных не ясно как сделать.

Я считаю что самым правильным решением будет простая утилитка, умеющая прогонять в нужном порядке sql-скрипты, и хранящая прямо в базе — что уже прогоняли.

Собственно мы так и делаем. Делаешь какие-то изменения — кладешь на SVN в спец-папку sql-diff-скрипт. Ну и потом всякие ccnet этими дифами нужную базу поднимают до нужной ревизии.

Ну и там у нее всякие еще плюшки могут быть, типа хранимки по отдельным файликам держать, справочники всякие из CSV-файлов для тестов заливать и прочее. И можно даже, для простых случаев, какой-то DSL вроде твоего, вместо sql-скриптов.

Собственно есть вот статья с основными мыслями по теме: www.codinghorror.com/blog/archives/001050.html
И Атвуда и Скотт Аллена (и не только их) я читал и не осуждаю.

По поводу «сложнее добавления колонки»: всегда можно выполнить платформозависимый SQL (версия Wizardby в транке уже это позволяет) и в нем все сложные действия и сотворить. При желании можно написать и скрипт для отката.

Проблема с SQL скриптами следующая: лично я при разработке постоянно меняю структуру БД (функциональность добавляю постепенно, небольшими пачками) и то же пересоздание структуры (например, дропнуть таблицу с парой уникальных индексов, парой default constraint'ов и несколькмим foreign key'ами, а потом поменять порядок столбцов или целиком ее переделать) превращается либо в унылое дёргание мышкой и ожидание, пока там всё это произойдет, либо в быстрое «wizardby ro» в консоли, правку MDL-скрипта и потом «wizardby u».
Примеряя к моим лично задачам, я вижу что оно работало бы только на начальном этапе разработки (т.к. не умеет мигрировать данные) и только ограниченно: тестовые данные не зальешь, view-хи и прочие объекты делать не умеет, и т.п).

Ну а если уж кому-то повезло и у него вот такими вот простыми операциями все обходится, я бы посоветовал ему, например, апдейтить схему прямо при запуске приложения, по отмепленным классам.
Старая схема — в базе. Новая схема — из классов. Смотришь разницу и делаешь дифф-скрипт. Делов-то.
тестовые данные не зальешь, view-хи и прочие объекты делать не умеет, и т.п
С данными и представлениями можно работать обычными средствами — SQL-скриптами.

В планах есть идеи насчет импорта данных из CSV-файлов в таблицы и комплексные «рефакторинги» схемы БД. Например, на текущем месте работы именно такими рефакторингами поддерживаются таблицы аудита, которые, по сути, являются полными копиями исходных таблиц, но все поля — nullable и лежат они в другой схеме. То есть, при добавлении поля в исходную таблицу оно автоматически добавляется и в таблицу аудита.

Смотришь разницу и делаешь дифф-скрипт
Дифф-скрипты для схем БД без ручного вмешательства работать не будут.
Понятно что дифф-скрипты автоматически в общем случае сгенерить нельзя. Но вот для удаления/добавления полей/таблиц/ключей, т.е. для того, что сейчас умеет твой DSL — вполне можно их сгенерить автоматом.
Если будет импорт данных и рефакторинги — тогда уже другое дело будет. А пока мне удобнее и привычнее написать:
alter table oxite_Language add LanguageName nvarchar(8) null
вместо:
add column LanguageName type => AnsiString, length => 8, nullable => false

Первое, к слову, даже короче выходит.

И вот так еще можно:
sp_rename «dbo.oxite_Language.LanguageName» «LangName» «Column»
Only those users with full accounts are able to leave comments. Log in, please.