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

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

Спасибо за статью!

Есть вопросы:
1. Вы так подробно расписали функционал программы, что невольно возникает вопрос: собираетесь ли вы поделиться ей с сообществом? :)
2. Ваша программа, фактически, помогает реализовать третий подход из упомянутой вами статьи («подход уподобления структуры БД исходному коду»). И пусть вы не храните в репозитории декларативную структуру БД, вы, тем не менее, генерируете соответствующий XML сами, и все так же используете diff-утилиту (являющуюся темой статьи) для миграции со старой версии на новую.
Как вы, наверное, помните из статьи, большой проблемой, которую я выделил в этом подходе, являлись изменения в данных. Было бы интересно услышать поподробней, как вы справились с этой проблемой, особенно учитывая, что уже три года используете этот подход.
1. Поделиться — пожалуйста! Утилита uniVersions входит в состав программного продукта UNA.md (http://una.md) — по вопросам приобретения обращайтесь по контактам, указанным на сайте.

Я расписал алгоритм так подробно, потому что считаю, что любые идеи не могут принадлежать кому-то одному, а являются достоянием всего человечества. И даже тот, у кого нет прав на программный продукт UNA.md, тем не менее имеет право знать, какие идеи в него заложены.

2. Действительно, это напоминает описанный в статье третий подход, хотя есть и различия. Фактически, к написанию статьи (моей первой статьи на хабре) меня подтолкнуло вот это предложение: «Отдельных статей, посвященных этому подходу, я, к сожалению, не нашел. Буду благодарен за ссылки на существующие статьи, если таковые имеются.»

Мне не нравится аналогия с исходным кодом. Исходный код не имеет такой четкой иерархической структуры, как структура БД, и описанный мной метод к исходному коду абсолютно неприменим.

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

Наверное, я неправильно использовал в тезисах термин «XML-образ». Для меня XML — это иерархическая структура в памяти, но кто-то понимает под этим только файл, то есть текстовое представление этой структуры.

Так вот, сравниваются не файлы, а объекты в памяти, экземпляры классов объектной модели.

3. Сравнение хранимых данных — это отдельная тема, описанная программа этим не занимается.

Дело в том, что взаимосвязи между данными, правила их идентификации и сравнения зависят от конкретного продукта, его бизнес-логики и не формализованы настолько четко, насколько мы это имеем в случае с метаданными Oracle.

Не думаю, чтобы можно было разработать сколько-нибудь универсальный инструмент для такой задачи.
1. Отдельно утилита не распространяется?

2. Есть один нюанс. Моя статья была написана с упором на использование систем контроля версий. Отсюда и аналогия с исходным кодом: в этом подходе декларативные определения меняются со временем, и их изменения можно отслеживать в VCS. В вашем же случае, как я понял, просто есть эталонная структура БД, на которую все БД клиентов мигрируют при помощи вашей утилиты.
Подход уподобления исходному коду я упомянул потому, что у вашего подхода в потенциале та же проблема: как быть, если перед или после изменения структуры какого-то объекта нужно выполнить трансформации данных?

3. Конечно же, речь не идет об универсальном решении. Но ведь в статье вы писали о своем подходе, применительно к вашему конкретному продукту, где вы этот подход практикуете уже несколько лет. Поэтому очень интересно было бы узнать ваш опыт. Часто ли возникают такие ситуации, когда автоматически изменить структуру БД недостаточно, и нужно еще и изменять данные? Как вы поступали в таких случаях?
1. Отдельно — нет, вроде бы… но напишите нашему руководству :-)

Я распространением не занимаюсь, мое дело — конструирование и кодинг, а остальное мне неинтересно :-)

2. Мы сравниваем не только с эталоном — нередко приходится сравнивать две версии одной клиентской схемы (например, оригинал — с офисной копией)

Если перед изменением структуры надо выполнить специфические действия — пишется скрипт (по старой технологии), при апгрейде пронумерованные скрипты выполняются до сравнения.

3. Такие ситуации возникают нечасто. Во-первых, меняя структуру, мы помним о десятках действующих клиентов. А в тех редких случаях, когда это случается, мы пишем скрипты, я о них уже упоминал несколько раз.

Фактически uniVersions выросла из программы, применяющей эти пронумерованные скрипты, и эта возможность в ней осталась, хотя и потеряла свою актуальность после разработки алгоритма сравнения с эталоном.
Спасибо за ответ, ваша ситуация более-менее ясна.
>>каждого клиента было собственное видение, какой должна быть его БД
Т.е. клиенты могли менять структуру БД самостоятельно?

>>Подключаясь к реальной БД,
Т.е. вы генерируете скрипт уже на клиенской машине? Тогда получается процесс полуавтоматический и происходит с участием конечного пользователя, если пользователь это — администратор СУБД, то тут понятно, а как быть если у него нет специальных знаний?
Ну, если все, что ему нужно сделать, — выполнить скрипт, то особых умений здесь не нужно. Но, учитывая, что БД были изначально рассинхронизированны как раз из-за действий клиентов, то похоже, что необходимые навыки и знания у них все же были.
Выполнить скрипт? а если при рефакторинге скажем поле стало NOT NULL? тут без человека никак не обойтись. А насчет знаний, тогда этот метод не подходит для массовой программы для среднестатистического пользователя.
Да, вопрос об изменениях в данных я задал автору в первом комментарии. Ждем ответа :)
Если поле вдруг стало NOT NULL, то тот, кто его таким сделал, должен был позаботиться о тех клиентах, у кого оно уже содержит пустые значения.

Тут есть два решения:
1. Полю задается DEFAULT, и перед применением NOT NULL вначале делается UPDATE всех пустых значений.
2. Программист сам пишет скрипт на заполнение этих пустых полей, и этот скрипт включается в пронумерованный список скриптов, которые применяется перед сравнением (старая методика)

Этот метод действительно не подходит для среднестатистического пользователя. Это инструмент для профессионалов — как осциллограф, например.
1. Да, именно так. Кроме того, что администраторы и штатные программисты клиентов свободно игрались с таблицами, триггерами и пакетами — так этим же занимались и наши внедренцы, которым важно было быстро решить проблему, заткнуть дыру… а там — хоть трава не расти.

Эти люди не до конца отдавали себе отчет в необходимости поддержания единой структуры БД у всех клиентов, не понимали или не хотели понимать, что любой последующий апгрейд, сделанный другим человеком, не знающим об этих локальных изменениях, просто сведет на нет все усилия предыдущего сотрудника.

2. Да, скрипт генерируется на клиентской машине. Да, процесс получается полуавтоматический, и может быть даже полностью автоматическим — если вы уверены, что на вашей БД нет локальных изменений в структуре. А если у человека нет специальных знаний — лучше бы ему не делать апгрейд.
Есть один большой недостаток, нельзя сравнить больше двух баз за раз.
Получается очень специфический инструмент.
Может быть чуть позже я расскажу о стандартизации сотен баз.
>>стандартизации сотен баз.
в общем случае это возможно, когда используется инкрементная по версиям миграция. Или тут имеется ввиду другое?
В какой-то момент у нас было с десяток баз с различающейся структурой. Чтобы синхронизировать их, мы выбрали одну, которая на глаз показалась ближе других к идеалу, и сказали себе: это будет Master Schema. Затем мы сравнивали ее с каждой из остальных БД и, в случае различий структур двух объектов, выбирали: оставить стуктуру объекта из Master Schema, или заменить на структуру из сравниваемой БД.
После того, как мы таким образом сравнили Master Schema со всеми базами, она приняла конечный вид. После этого для всех БД был сгенерирован diff-скрипт и применен. В итоге все структуры БД синхронизировались.
Для сравнения структур и для генерации diff'ов мы использовали SQLyog, в котором есть утилита, подобная той, что автор описывает в статье, только для MySQL.

Стоит отметить, что, в зависимости от количества объектов БД и количества существующих БД, имеет смысл выбирать либо вертикальный, либо горизонтальный подход — или сравнивать всю структуру базы, синхронизируя по 2 БД за раз, или синхронизировать несколько объектов за раз, но всех базах данных.

Я так понимаю, у вас есть опыт синхронизации структур сотен баз данных? Было бы очень интересно послушать. В нашем случае их был всего десяток, и синхронизация все равно отняло порядочное количество времени.
Я не назвал бы это «большим недостатком».

А зачем их сравнивать «за раз»? Чтобы сделать «один универсальный скрипт»? А чем плохо для каждой схемы генерировать на лету персональный скрипт — благо это делается за считанные секунды?

У нас есть стандартная схема, ее XML-образ помещается в каждый новый релиз продукта.

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

Тут уже нельзя все сделать «одной кнопкой», надо вникать в различия и разбираться, что в какую сторону синхронизировать.
Предложенный вами подход очень похож на Liquibase, за исключением пользовательского интерфейса и возможности синхронизировать только часть объектов. Очень интересно.
Так для себя же писалось :-)

У меня была задача навести порядок в структурах баз, и я придумал и сделал этот инструмент не «для галочки», а чтобы решить проблему
Для предотвращения проблем с рассинхронизацией схемы БД на разных площадках у нас на проекте используется утилитка oracle-ddl2svn. Автоматическое отслеживание изменений схемы БД Oracle в системе контроля версий SVN. Схема БД храниться в виде DDL скриптов.

Плюс программы, что она делает все автоматически. Т.е. настраивается периодическое задание и потом остается только смотреть, что поменялось в схеме за указанный период.

Утилиту написал я в начале года. Вначале искал что-нибудь уже готовое, но ничего не нашел. Решил потратить пару выходных и написать свое. В итоге потратил больше, но написал довольно универсальную программку. Выложил ее на гуглокод, опубликовал ссылку на sql.ru и stackoverflow.com. Народ ей пользуется и даже присоединился коммитер из США )
>>Плюс программы, что она делает все автоматически
а как она автоматически разруливает такие неопределенности типа изменения поля c NULL на NOT NULL???
>>а как она автоматически разруливает такие неопределенности типа изменения поля c NULL на NOT NULL???

Автоматически oracle-ddl2svn ничего не разруливает. Только сообщает, что есть изменения.
Diff'а для генерации скрипта по изменениям DDL в программе нет.
Кстати, такой diff есть в PL/SQL developer и в TОAD.

А так DDL поменяется, например, было
CREATE TABLE "CTC"."TABLE1" 
   (	"ID" VARCHAR2(16));

Стало
CREATE TABLE "CTC"."TABLE1" 
   (	"ID" VARCHAR2(16) NOT NULL ENABLE);

Это зафиксируется в SVN
т.е. окончательный скрипт миграции пишется все равно руками?
да
«Плюс программы, что она делает все автоматически»
Как выяснилось, делает она у вас далеко не «все» :-)
«Все» — неточный термин =)
У меня цель была не допустить рассинхронизацию. Достигается с помощью oracle-ddl2svn.
А тут топик про то, как сводить разные версии к одной.

Профилактика обычно дешевле лечения.
Профилактика — это не дать вносить изменения в обход стандартного протокола.

А когда изменения уже есть — пусть их показывает ваша или моя программа — это уже наступила фаза для «лечения».

У нас нет системы контроля версий, а разработка часто ведется прямо на сервере клиента, и обновленные триггера и пакеты часто там и остаются (а потом теряются при апгрейде и восстанавливаются после криков «Караул!» методом сравнения с образом вчерашней структуры).

Работают у нас вчерашние (а часто и сегодняшние) студенты, у которых высшая оценка своему труду — «Все работает». А как именно оно работает, и как это отразится на других — им невдомек. Текучка кадров — объективная реальность.

Поэтому фактически все апгрейды выполняют специально обученные люди как раз в полуавтоматическом режиме, сливая отличающиеся объекты по одному. Shift+Click :-)
Так может быть, вам стоило бы задуматься о центральном репозитории кода, в который бы и сабмиттились изменения БД? А еще, говорят, ревью кода — хорошая практика :)
Кто-то из нас кого-то не понимает. Попробуйте говорить по-русски :-)

Есть у нас «Центральный репозиторий» — это стандартная схема, куда никому нет доступа, кроме меня и еще пары человек. Но изменения в базах делаются у клиентов как решение мелких задач, а я потом заливаю отличия в стандартную схему в офисе, откуда потом они попадают ко всем остальным.

«Ревью кода» — рефакторинг — я делаю регулярно, когда сливаю отличия в пакетах. Если нужно залить только часть текста, то там моя программа бессильна — точнее, она только показывает отличия, а я открываю пакет в Toad или PL/SQL Developer, вручную переношу туда изменения и снова сравниваю измененный пакет в uniVersions
Да, вы действительно меня не так поняли. Под центральным репозиторем кода я имел в виду именно систему контроля версий. Это полезно для, как ни странно, контроля версий :) и хранения их истории. Вы видите все изменения, кто и когда их внес.

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

Впрочем, хозяин — барин. Это был всего лишь совет из личного опыта.

P.S. Ревью кода (code review) — это совсем не рефакторинг. Во всех командах, где я его встречал, code review производился ДО сабмита в репозиторий.
У нас пытались внедрить CVS, но не пошло (только мешало). В другой фирме, где я подрабатывал, все с ним работают, но мне оно не показалось удобным. Наверное, все дело в общей культуре всех участников процесса :-)

У нас фактически это и происходит, я сам и есть CVS. Я сам заливаю изменения в стандартную схему, и сам раздаю новые релизы с обновленными образами структуры БД.

P.S. Спасибо за разъяснение. Указание момента, когда именно выполняется code review, сразу объясняет, что оно такое :-)
ru.wikipedia.org/wiki/Code_review
Правда, там не очень чётко сказано, что это когда код одного программиста обязательно просматривается другим программистом, как правило каждый смотрит чей-то код.
А если без шуток, то у меня такое ощущение, что это вы меня не поняли. И ваши пояснения только укрепляют меня в этом ощущении.

>> Так может быть, вам стоило бы задуматься о центральном репозитории кода, в который бы и сабмиттились изменения БД?

Вообще, ваш насмешливый тон здесь неуместен. Естественно, что над этой проблемой работали несколько неглупых людей в нашей фирме, и поверьте мне, что они задумывались о многих вещах, говорить о которых не хватит места в этой статье.

Наша стандартная схема — как раз и есть то, о чем вы говорите, называя ее Центральным репозиторием, и она выполняет как раз те же самые функции.

То, что вы называете словом «сабмититься», происходит, когда я, по просьбе кого-то из разработчиков, заливаю новые изменения в стандартную схему — используя все тот же uniVersions.

>> А еще, говорят, ревью кода — хорошая практика :)

И снова ваш насмешливый тон неуместен. Разумеется, при обнаружении отличий в телах триггеров или пакетов я вначале вникаю в суть этих отличий, и только потом вношу изменения (то есть анализ происходит именно до сабмита).

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

Я написал про центральный репозиторий и ревью кода потому, что у вас разработка ведется прямо на серверах клиентов.
У вас уже есть «генеральная» версия схемы — отлично. Вы проверяете все изменения перед применением на этой схеме — прекрасно. Но в таком случае, вам стоило бы сделать этот процесс обязательным перед редактированием объектов БД на серверах клиентов. Ведь всегда можно создать тестовую БД, протестировать изменения на ней, а уже затем отдать вам в центральную схему, после чего последует синхронизация изменений на все БД клиентов.
Это бы решило проблемы, о которых вы сами же и упомянули.

Впрочем, я вам ничего не навязываю. Пожалуйста, воспримите это как совет. Если он вам не подходит — он вам не подходит.
Мы именно так и стараемся делать.

Точнее, когда это делаю я, а не студенты.
Извините за резкость, я старался быть настолько толерантным, насколько мог :-)

>> на каждом интернет-ресурсе — свой стиль общения и свои нормы

Смею предположить, что не стиль меняет людей, а наоборот.

«Не стоит прогибаться под изменчивый мир» :-)

Еще раз извините, если я был неправ
У нас SQL Server и мы сталкивались с подобными проблемами. Для их решения мы пользовались готовыми продуктами от Redgate, возможно они подойдут и вам.
Сейчай schema compare для SQL Server встроена в VisualStudio 2010.
Для Oracle есть Toad, который умеет делать оочень много полезных вещей по работе со схемами. В свое время мы делали полный экспорт схем двух баз в скрипты, а затем сравнивали обычным diff-ом. Хотя у Toad-а для этого есть специальная тулза.
Если бы тоад не был таким глючным (да-да-да) и дорогим, он был бы самым лучшим-лучшим. Ну а так он просто лучший.
Я давно пользуюсь и Toad, и PL/SQL Developer — но той функциональности, что мне нужна, там и близко нет
Toad вроде бы написан на Delphi с применением Developer Express, так что часть глюков можно списать на эти две составляющие :-)

Мне тоже он очень нравится — для целей администрирования он «лучший».

Но для работы с PL/SQL лучше и удобнее PL/SQL Developer — писать пакеты я предпочитаю в нем.

Я уже говорил, что у меня сравниваются не файлы, а объекты в памяти программы, написанной на С++

Только так я могу сам управлять процессами идентификации и сравнения объектов метаданных.

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

Вначале процитирую абзац из самой статьи:

В основе идентификации сравниваемых узлов лежат два критерия — общий родительский узел и одинаковое имя самого узла. Имя узла используется и для сортировки дочерних узлов одного родительского узла. Поэтому именованию узлов разных типов объектов придается особое значение.

Отсюда есть следствия:
— у каждого узла есть текстовое имя (регистронезависимое)
— среди дочерних узлов одного родительского узла все имена уникальны

При формировании имени объекта важно ухватить его суть. Например, для таблиц или пакетов это просто имя самого объекта. Переименован — значит, уже «не он». Отсюда тоже есть следствия: нельзя просто так переименовывать объекты во избежания их дублирования. Например, мне jlyf;ls не понравилось имя триггера INSTEAD OF на какой-то VIEW, и я его переименовал. И через какое-то время на схемах стала гулять эта VIEW с обоими триггерами, дклающими одно и то же (естественно, это ошибка).

Есть объекты, для которых их имя в БД неактуально с точки зрения идентификации. Это характерно для уникальных объектов или объектов, не допускающих дублирования. Например, первичный ключ, как бы он ни назывался, должен сравниваться только с другим первичным ключом той же таблицы. Поэтому имя соответствующего узла строится как имя его типа: «Primary key». Имена уникальных констрейнтов строятся как имя типа «Unique» плюс список полей, по которым построен этот констрейнт. Так же и для индексов, и так далее.
Занимаюсь такой фигнёй тоже.

Реализовано так. Подходит разработчик, говорит: «Надо мне бы вот такое сделать». Разработчиков к DDL не пускаем. Я делаю, сохраняю скрипт изменений под номером его задачи в trac. Разработчик выставляет дополнительное поле в trac в «Проводилась модификация БД». После integrated и его заливкой на ветку, я выбираю по вышеуказанному признаку в trac его номер тикета и просто выполняю скрипт на тестерской, девелоперской, других базах.

Казалось бы слабое место, это модификация пакетов, где каждый девелопер может попросить поменять свою часть, но это уже ручками. Часть пакета не поменять =)

Спасибо за статью и, особенно, за комментарии, щаз пойду копать новые знания.
Здесь ключевая фраза — «Разработчиков к DDL не пускаем» :-) У нас уже пустили, и обратного хода нет.

Да, пакеты не имеют такой четкой иерархической структуры, поэтому у нас есть ответственные за модули, и изменения в закрепленных за ними пакетах проходят через них… а остальное — через меня :-)
Complete Compare в ErWin ведь как раз это и делает, сравнивает структуру в ErWine и в базе, достаточно не отлынивать от рисования таблиц перед вставкой их в БД.
Отправил раньше времени:
генерирует скрипт для изменений/вставки/удаления и прямо из интерфейса ErWin можно запустить экспорт.
Но вот на другие удаленные точки этот скрипт приходится расскидывать вручную.
Люди! У меня сравниваются не только таблицы :-) а и такие вещи, как synonym, public synonym, public grant, policy, context, role, privilege…

Универсальные инструменты убоги как раз в силу своей универсальности. Наш инструмент заточен под Oracle, причем только на 10g и выше — мы не решали задачу универсальности, а делали вещь «для внутреннего употребления» :-)

Зато можно «отлынивать» и ничего не рисовать — я могу сравнивать даже схемы, которые делали другие люди.
ErWin сравнивает и синонимы и гранты и т.д.
Триггеры, процедуры и пакеты только в ErWin не ведем.

Рисовать полезно, когда будет 5000 с лишним таблиц, это будет серьезная помощь.
контексты ErWin сравнивает? секвенсоры, типы, привилегии схем и ролей?

и потом, я уже говорил — мне для сравнения схем не нужно ничего, кроме самих схем, и это очень удобно.
Для Firebird/Interbase есть похожий инструмент в IBExpert. Часто им пользуюсь.
Большинство инструментов только показывают отличия, не решаясь что-то менять.

uniVersions позволяет синхронизировать объект «одной кнопкой» (фактически — Shift+Click)
Эта комбинированная операция делает три действия:
— генерирует скрипт на разницу
— выполняет скрипт на сервере
— обновляет описание объекта

Как результат — синий или красно-белый значок на дереве становится чисто белым, а пользователь переходит к другому «цветному» значку.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории