|
||||||||
|
![]()
|
![]()
Службы преобразования данных в Microsoft SQL Server 7.0Алексей Шуленин, Microsoft
Введение Независимо от наличия или отсутствия корпоративных стандартов на предприятии разнообразие способов хранения и представления информации обусловлено рядом объективных обстоятельств, в частности, эволюционным характером развития информационной системы предприятия, например: табличный процессор -> персональная СУБД -> настольный (на 5-10 клиентов) БД-сервер -> полнофункциональный мощный сервер баз данных. При этом каждый следующий виток прогресса, как правило, не означает разрушение до основания старой системы во имя построения новой, какой бы производительной и масштабируемой она ни обещала быть. Перефразируя известное высказывание о душе математика, можно сказать, что за душу каждого программиста борются ангел бесконечного совершенства и дьявол здорового консерватизма. В любом случае внедрение новой технологии - процесс далеко не одномоментный, и в течение переходного периода необходимость максимально мирного сосуществования старых и новых форматов хранения в рамках одной информационной системы не вызывает сомнений. Более того, в подавляющем большинстве случаев унаследованные приложения не умирают бесследно, а продолжают тихо доживать свой век на каких-нибудь узкоспециализированных локальных задачах, данные из которых тем не менее должны быть доступны нашей комплексной современной и перспективной системе. Другой вариант развития событий. Есть задача З1, для которой покупается решение фирмы Ф1 на платформе БД1. Через некоторое время возникает задача З2, которой фирма Ф1 не занимается, но под которую есть решение у фирмы Ф2, только вот беда - производится оно на БД2 и ни на чем ином. Повздыхали, конечно, но делать нечего - купили. Так понемногу у нас на предприятии вместе с решениями собирается изрядный зоопарк СУБД. А спустя еще некоторое время появляется новая задача З3, которой на входе требуются данные из БД1, БД2 и т.д., и вместо программирования бизнес-логики, разработчики вынуждены решать проблемы доступа к разнородным форматам данных. Я думаю, приведенные примеры наверняка до боли знакомы большинству программистов, администраторов и IT-менеджеров. Разумеется, ими не исчерпываются все возможные сценарии, однако этого вполне достаточно для того, чтобы существование и актуальность проблемы интероперабельности различных систем управления данными считать доказанным. В сложившейся ситуации наиболее привлекательным и экономичным решением оказывается продукт, способный работать с данными любой природы независимо от их формата и места хранения. Этому требованию удовлетворяет Microsoft SQL Server 7.0, взаимодействующий как с собственным, так и с внешними механизмами хранения с помощью технологии универсального доступа OLE DB. Однако предметом нашего сегодняшнего разговора будет не сама технология OLE DB (предполагается, что слушателям она в той или иной степени известна), а ее реализация в Microsoft SQL Server 7.0 и открывающиеся в связи с этим возможности перед разработчиками гетерогенных серверных приложений. Если отнести DTC-транзакции к тиражированию с максимально плотной (по протоколу двухфазной фиксации) целостностью, я бы выделил на стороне сервера три основных способа построения распределенных приложений - это тиражирование, службы преобразования данных (Data Transformation Services - DTS) и распределенные запросы. Все они логически связаны друг с другом: например, службы преобразования данных можно рассматривать как разновидность тиражирования мгновенных снимков данных (snapshots), а пакет DTS или шаг пакета может выступать в роли прилинкованного сервера. Каждый из них может в той или иной мере использоваться в реальных приложениях в процессе построения хранилищ данных при сборе и консолидации информации из внешних и распределенных OLTP-источников, однако наиболее яркая роль здесь безусловно принадлежит службам преобразования данных. DTS: назначение и возможности. Основной и наиболее сложной задачей, для решения которой применяются DTS, является создание хранилищ или витрин данных. Данные, которые наполняют хранилище, могут исходно быть представлены самыми различными форматами и системами хранения, как мы видели во Введении. Пусть, например, учетом торговых операций занимается приложение на Oracle, а отдел маркетинга отслеживает свои мероприятия с помощью Access. Мы хотим построить куб в MS OLAP Services, чтобы проанализировать как те или иные маркетинговые мероприятия сказываются на объемах наших продаж. Мораль - мы должны уметь осуществлять доступ к источникам данных различной природы. Перед тем, как положить данные в хранилище, данные должны быть очищены, проверены на непротиворечивость и приведены к единому виду. Например, одно приложение хранит месяц апрель как "Апрель", другое - как "апр", третье - как "April", четвертое - как "IV", пятое - как "04" и т.д. Даже если мы с помощью OLE DB, ODBC или как-то иначе доступились до всех наших разнородных источников данных, даже если (представим себе неправдоподобно хорошую ситуацию) это все хранится в одних форматах одной СУБД, мы получим несколько разных членов одного временнОго измерения, которым на самом деле соответствует всего лишь один член уровня "месяц". А ведь месяц может вообще храниться не отдельно, а в составе даты, например, 01/04/99, и тогда его нужно выцепить оттуда при наполнении хранилища. Еще одно требование - возможность частичной предагрегации. Представим себе, что продуктовое приложение хранит данные о продажах за каждый день. Но в процессе анализа будущего куба мы не собираемся погружаться до дней, нас вполне устроит месячный дискрет. Следовательно, нечего тащить все детали в куб и раздувать объем хранилища, стоит агрегировать месячные продажи еще на стадии наполнения хранилища. Итак, доступ к разнородным данным, их очистка, проверка на непротиворечивость, унификация и предагрегация - все эти задачи решают службы преобразования данных. В простейшем случае DTS могут рассматриваться просто как средство импорта/экспорта данных в/из SQL Server. По большому счету этот инструмент можно применять безотносительно к SQL Server для переноса данных из одного источника в другой, например, из Oracle в Access, из dbf-формата в текстовый файл и т.д. Возникает законный вопрос - как обстоит дело с переносом метаданных и объектов БД, таких, как хранимые процедуры, триггера, индексы, правила ссылочной целостности и прочие ограничения? В случае, когда и источник, и назначение оба представляют собой один или разные SQL Server 7.0, перенос объектов не составляет труда и выполняется задачей типа Transfer SQL Server Objects. В остальных случаях автоматически решается только перенос схемы данных, например, структуры таблицы (и, само собой, данных в ней, как уже было сказано). Дело в том, что функциональность источника/назначения, не являющегося SQL Server, априори неизвестна DTS. Ну, например, что такое индекс, если речь идет об ODBC-драйвере для ASCII-файла? Поэтому если источник, скажем, тот же Oracle, поддерживает данный тип объекта, его перенос возможен, но потребует небольшой ручной работы, например, ввести в состав пакета задачу типа Execute SQL и написать в ней соответствующий SQL-скрипт. При переносе схемы данных отображение типов полей источника на типы полей назначения выполняется автоматически путем приведения и тех и других к стандартным типам OLE DB, за что отвечают OLE DB-провайдеры источника и назначения. Базовой административной единицей DTS является пакет (package). Основными компонентами пакета служат соединение (connection), задача (task) и шаг (step). Пакет содержит самодостаточное описание всех входящих в него соединений, задач, шагов и других компонентов. Соединения Соединение как компонент пакета представляет собой описание источника или назначения, использующихся при переносе данных или других задач. В дальнейшем для краткости мы иногда будем употреблять термин "источник" независимо от того, идет ли речь об источнике или назначении. Соединение с источником осуществляется через OLE DB, следовательно DTS может работать с источниками данных любого типа и формата, в том числе нереляционными, для которых существует OLE DB-провайдер (или ODBC-драйвер, так как в этом случае будет задействован MSDASQL - OLE DB-провайдер для ODBC). Наиболее часто встречающиеся типы источников имеют в DTS предопределенные соединения, что означает, что соединение уже почти полностью инициализирует провайдера, оставляя пользователю ввести DATASOURCE или какую-то часть PROVIDERSTRING. К таким источникам относятся различные версии MS SQL Server, Oracle, Excel, Access, Paradox, dBase и текстовые файлы. Для остальных требуется описание ODBC DSN (тип - Other Connection) или OLE DB-соединения через Microsoft Data Link (.udl-файлы). Последние позволяют провайдеру настроить пользовательское интерфейс для конфигурирования соединения. SQL Server 7.0 поставляется с OLE DB-провайдерами для SQL Server, OLAP Services, Jet версий 3.51 и 4.0, Index Server, Site Server, Oracle, ODBC и некоторыми другими, например, для самих DTS. Таким образом, все перечисленные источники доступны и могут быть использованы при работе DTS. В настоящее время число ресурсов, для которых написаны OLE DB-провайдеры, перевалило за полсотни и продолжает расти. Понятно, что всех их включить в SQL Server нереально. Поэтому, если Вам требуется соединение с ресурсом, OLE DB-провайдер для которого не включен в комплект поставки SQL Server 7.0 и не выпускается фирмой-производителем Вашей СУБД, следует обратиться на страницу http://www.microsoft.com/data/oledb/products/product.htm, где приведен максимально полный список доступных OLE DB-провайдеров для всех распространенных источников данных, таких, как Sybase, Informix, DB2, Rdb, Ingres, Red Brick, Non-Stop SQL, Adabas, Jasmine, FoxPro, Clipper, VSAM AS/400, Lotus Notes, MS Exchange, службы каталогов NetWare и Windows NT и др., производства как Microsoft, так и других фирм, например, ISG, Intersolv, Object Design Inc., Sagent Technology, Sequiter, X-Tension и т.д.
В более сложных ситуациях, например, когда Вы работаете с источником данных собственной архитектуры и разработки, OLE DB-провайдер придется писать самим с помощью Data Access SDK (входит в состав Microsoft Data Access Components 2.1, может быть свободно скачан с http://www.microsoft.com/data/download.htm). Пример создания соединения в DTS Designer показан на рис.1 Задачи
В известном смысле Тransformation Тask можно рассматривать как частный случай задачи типа ActiveX Script, которая может содержать произвольный код на любом из упоминавшихся скриптовых языков с использованием встроенных объектов DTS, таких, как уже знакомые нам DTSSource, DTSDestination, DTSErrorRecords и т.д. Полный перечень этих объектов с описанием их методов и свойств содержится в документации на SQL Server (см. DTS Reference). Вот как мог бы выглядеть перенос данных "один в один" (поля приемника такие же, как поля источника) из одного формата в другой, оформленный в виде задачи ActiveX Script на JavaScript: function main() { for ( var i = 1; i <= DTSSource.Count ; i++ ) DTSDestination(i) = toUpperCase(DTSSource(i)); return(DTSTransformStat_OK); } В ходе переноса выполняется приведение строковых значений полей к верхнему регистру. Задача типа SQL Script может содержать любую последовательность команд на языке Transact-SQL. Отметим, что при ее создании Вы можете воспользоваться графическим построителем SQL-запросов (см. рис.4), что значительно облегчает процесс написания кода запроса.
Еще один тип задачи - это Data Driven Query Task (DDQ), название которого можно примерно перевести как определяемый данными запрос. Принцип ее действия легко понять также опираясь на рассмотренную нами выше Transformation Task. Последняя, как мы уже знаем, берет каждую запись из множества записей источника, делает (или не делает) над ее полями какое-либо преобразование и получившийся результат добавляет в назначение, т.е. выполняет INSERT. Иногда просто INSERT оказывается недостаточным. DDQ поддерживает 4 типа запроса (INSERT, UPDATE, DELETE и User Defined), которые оперируют над полями источника в зависимости от результата преобразования и которые прописываются в закладке Queries диалогового окна Data Driven Query Properties. Например, INSERT INTO Categories(CategoryID, CategoryName, Description, Picture) VALUES (?,?,?,?) UPDATE Categories SET Description = 'Mustard' WHERE CategoryID = (?) AND CategoryName = 'Produce'.
SELECT au_id FROM authors WHERE (au_lname = ?) Остается только выбрать соединение из числа определенных внутри данного пакета (может быть отличным от источника и назначения), на котором будет выполняться этот запрос, и как-нибудь обозвать параметр поиска, например, MyLookup. Теперь в коде преобразования можем написать: DTSDestination("au_id")= DTSLookups("?yLookup").Execute(DTSSource("au_lname").Value) При этом будет выполнен приведенный выше запрос, служащий определением параметра поиска, которому на входе будет передано текущее значение поля au_lname. Результат (значение au_id) будет присвоен в соответствующее поле приемника. Что затем произойдет с полями приемника - зависит от типа задачи. В случае Transformation они однозначно будут добавлены в назначение, в случае DDQ - зависит от кода возврата, т.е. от нас.
Понятно, что сколько бы типов задач ни было предусмотрено в пакете, в жизни всегда найдется место подвигу, т.е. встретится ситуация, когда этого будет недостаточно. Поэтому в пакет введено понятие базового интерфейса IDTSCustomTask, от которого, кстати, произведены все рассмотренные нами типы задач. При написании собственного типа задач Вы поступаете точно так же, наследуя данный интерфейс, после чего спокойно совершаете свой подвиг, реализуя коллекцию свойств, метод Execute и т.д. Кстати, чтобы в графическом интерфейсе DTS Designer было удобнее работать с созданным Вами типом задач, стоит обратить внимание на DTSCustomTaskUI. Шаги. Шаг есть задача, рассмотренная с точки зрения последовательности выполнения. Шаги позволяют реализовать простейший workflow внутри пакета, т.е. ветвление логики выполнения задач в зависимости от результата работы предыдущих этапов (см. рис.6). Несмотря на то, что объектная модель DTS рассматривает шаг как самостоятельную, отличную от задачи сущность, нам будет удобно представлять себе шаг в виде обертки вокруг задачи. Одному шагу может соответствовать 0 или 1 задача, одну задачу, вообще говоря, могут инкапсулировать несколько различных шагов. ActiveX-код этой обертки (контекстное меню задачи шага, пункт Workflow Properties, закладка Options, Use ActiveX Script -> Properties) выполняется ранее привязанной к ней задачи, что позволяет нам на основе результатов этого кода принять решение о способе выполнения соответствующей задачи: пропустить, выполнить пять раз, выполнять, пока не ... и т.д. Переход от одного шага к другому может происходить в случае успешного завершения предыдущего шага, в случае неудачного завершения, либо просто по завершении независимо от результата. Например, первый шаг включает в себя ActiveX Script-задачу создания базы данных с помощью SQL-DMO, в случае успешного создания производится переход к шагу, состоящему из SQL-задачи создания таблиц, в случае неудачи выполняется шаг с задачей SQL Mail, отсылающей сообщение администратору. Графически управление последовательностью выполнения производится из меню DTS Designer (пункт Workflow) в режиме редактирования пакета. Программно это достигается с помощью коллекции PrecedenceConstraints, входящей в состав свойств шага. Каждый объект PrecedenceConstraint имеет свойства StepName, в котором указывается имя шага, от которого будет зависеть выполнение данного шага, и PrecedenceBasis, где прописывается, что будет приниматься в рассмотрение: просто завершение того шага, либо его результат. Возможны ситуации, когда шаг имеет право выполняться только если не один, а несколько предшествующих шагов закончились, допустим, успешно, или наоборот, когда в результате выполнения одного шага происходит переход сразу к нескольким шагам, как мы видим на рис.6 (зеленые пунктирные стрелки означают "при успехе"). В этом случае сразу несколько шагов, т.е. задач будут выполняться параллельно, например, данные из Oracle и Sybase будут одновременно грузиться в таблицы на SQL Server на разных потоках внутри одного процесса. (Процесс соответствует пакету). Некоторые OLE DB-провайдеры не поддерживают модели free-threading, например, Microsoft Jet OLE DB Provider (он используется для доступа к данным Access, Excel, dBase, Paradox, HTML). Для них безопаснее запретить параллельное выполнение, выбрав опцию Execute on main package thread. По умолчанию, все потоки выполняются с одинаковыми приоритетами, равными приоритету процесса, но для более важных и критичных задач можно подправить относительный приоритет. Это делается из контекстного меню задачи, пункт Workflow Properties, закладка Options, бегунок Task Priority. Приоритет всего пакета в целом управляется из диалогового окна DTS Package Properties, закладка General, группа Execution Parameters, бегунок Priority Class. Там же определяется максимальное число параллельно выполняющихся задач (по умолчанию 4). Если не прибегать к DTS Designer, то все это можно сделать изнутри приложения с использованием DTS API: свойства DTSPackage.PackagePriorityClass и DTSStep.RelativePriority. Пакеты: создание и хранение
включая сам пакет в бинарном виде (поле PackageData типа Image). Последний, третий, вариант представляет собой хранение пакета в виде файла СОМ-структуры. По умолчанию ему дается расширение .dts. Это наиболее удобный способ хранения пакетов при их копировании, пересылке по e-mail и т.п. В одном файле могут храниться пакеты с разными именами и разные версии пакета с одним именем. Версию можно загрузить в DTS Designer из контекстного меню раздела Data Transformation Services: Аll Tasks -> Open Package -> выбрать файл на диске -> выбрать пакет из числа хранящихся на диске и, при необходимости, его версию (см.рис.9). Пакеты: выполнение и безопасность Равно как и создавать, выполнять пакеты можно из графического интерфейса, из командной строки и программно. Если пакет был запущен на выполнение из DTS Designer или DTS Import/Export Wizard, он выполняется в контексте mmc.exe, за вычетом тех случаев, когда мастер импорта/экспорта был вызван не через ММС, а из командной строки (dtswiz.exe). Для запуска пакета из командной строки применяется утилита dtsrun.exe. Основными ключами служат /S - имя сервера; /U - имя пользователя на сервере; /P - пароль на сервере; /E - доверительное соединение (аутентификацию гарантирует Windows NT); /N - имя пакета; /V - GUID версии; /M - пароль доступа к пакету; /R - имя БД, где лежит репозиторий, сигнализирует о том, что мы выполняем репозиторный пакет; /F - в случае хранения пакета в виде СОМ-файла, его UNC-имя и т.д. Подробнее см. dtsrun /?. В этом случае пакет выполняется в контексте командного окна. Хранилище должно пополняться в строго определенные моменты времени, поэтому для пакетов DTS существует возможность назначить расписание их выполнения (контекстное меню пакета -> пункт Schedule Package). При этом в SQL Agent создается новое задание с именем пакета, состоящее всего из одного шага типа Operating System Command, который содержит команду dtsrun /~N <имя пакета> и т.д. В этом случае выполнение пакета происходит в контексте сервиса SQLAgent. Для выполнения пакета из приложения применяется метод Execute объекта DTS.Package. Существуют два уровня безопасности пакета: владелец (owner) и пользователь (user). При сохранении пакета ему можно назначить как пользовательский пароль, так и пароль владельца. Уровень владельца предоставляет права на редактирование и выполнение пакета, уровень пользователя - только на выполнение. Задание уровня безопасности владельца вызывает шифрование значений всех свойств пакета, кроме Name, Description, ID, VersionID и CreationDate. Следует обратить внимание, что знание пароля пользователя или владельца является необходимым, но недостаточным условием для работы с пакетом. Если пакет локальный, либо хранится в репозитории на SQL Server, для его запуска должны существовать права на чтение в этой базе, а для редактирования - на запись. Если пакет находится в .dts-файле, то должны иметься соответствующие права операционной системы на доступ к этому файлу. Пакеты: некоторые прикладные вопросы Завершая наш рассказ о службах преобразования данных, мы остановимся на трех прикладных вопросах использования пакетов DTS, именно: как включить задачу в транзакцию, как программным образом перебрать пакеты и как заставить шаг выступить поставщиком данных для внешнего потребителя. Шаги определяют последовательность выполнения задач пакета. Как только речь заходит о последовательности действий, встает вопрос о том, нельзя ли рассматривать эту последовательность как единую транзакцию. Шаги обладают свойствами, позволяющими объединять их в транзакцию и в зависимости от результата работы шага фиксировать, либо откатывать результаты предшествующих шагов. Как и все распределенные транзакции, транзакции внутри пакета DTS выполняются с помощью MS DTC, поэтому, если Вы собираетесь их использовать, проследите, чтобы его сервис был стартован (snap-in SQL Server -> Support Services -> Distributed Transaction Coordinator). Для того, чтобы соединение, с которым работает задача в шаге, понимало команды фиксации и отката транзакции и могло наследовать контекст внешней транзакции пакета, требуется, чтобы OLE DB-провайдер данного соединения поддерживал интерфейс IТtransactionJoin. В противном случае при выполнении пакета Вы получите сообщение об ошибке: Connection : for task : doesn't support joining distributed transactions or failed when attempting to join. No such interface supported. Кроме того, несмотря на то, что в ходе данного конкретного выполнения пакета несколько шагов могут выполняться параллельно, в пакете в каждый момент времени допускается одна и только одна транзакция. Таково ограничение текущей версии DTS. Это не значит, что в пакете не может быть несколько транзакций, это означает лишь, что в настоящее время они могут выполняться лишь последовательно, друг за другом. Следовательно, когда мы устанавливаем workflow в пакете, необходимо внимательно следить, чтобы ни при каких исходах и ветвлениях логики наши последовательные транзакции не наложились друг на друга. Для разрешения транзакций внутри пакета нужно отметить опцию Use Transactions в диалоговом окне свойств пакета, закладка Advanced. Там же задается уровень изоляции транзакций (Transaction Isolation Level), по умолчанию он установлен в Read Committed. Объединение шагов в транзакции осуществляется с помощью контекстного меню задачи шага, пункт Workflow Properties, закладка Options, опция Join Transaction If Present. Завершение транзакции на данном шаге с фиксацией или откатом изменений данного и предыдущих шагов транзакции управляется опциями Сommit transaction on successful completion of this step и Rollback transaction on failure. Предположим, мы отметили обе эти опции и данный шаг состоит из ActiveX Script Task. Тогда при Main =DTSTaskExecResult_Failure транзакция будет откачена, при Main = DTSTaskExecResult_Success - фиксирована. Тех же результатов можно добиться программным путем, используя практически одноименные свойства объектов Package и Step. Следующая практическая задача, о которой мне бы хотелось поговорить, - это задача перебора пакетов. Ошибается тот, кто считает, что для этого достаточно пробежаться по какой-нибудь коллекции вроде oSrv.DTS.Packages. Чтобы понять, почему все не так просто, вспомним, что существует три способа хранения пакетов. Работа с пакетом в приложении осуществляется так: в проекте делается ссылка на Microsoft DTSPackage Object Library, создается пустой объект типа пакет, после чего одним из трех методов в зависимости от способа хранения происходит загрузка экземпляра интересующего нас пакета. Private Sub cmdLoadPkg_Click() Dim dtsPkg As New dts.Package, i Select Case lstStorageType.ListIndex Case 0 'На сервере как локальный пакет dtsPkg.LoadFromSQLServer ServerName:=txtSrvName.Text, _ Flags:=DTSSQLStgFlag_UseTrustedConnection, _ PackageName:=cmbPkgName.Text Case 1 'Репозиторий dtsPkg.LoadFromRepository RepositoryServerName:=txtSrvName.Text, _ RepositoryDatabaseName:="msdb", _ RepositoryUserName:="dbo", RepositoryUserPassword:="", _ PackageID:="", _ Flags:=DTSReposFlag_UseTrustedConnection, _ PackageName:=cmbPkgName.Text 'dtsPkg.LoadFromRepository Case 2 'В виде СОМ-объекта (.dts-файл) dtsPkg.LoadFromStorageFile UNCFile:=txtFileName.Text, Password:="" End Select EnumPkgCnns dtsPkg End Sub
Cама форма приведена на рис.10. Мы выбираем способ хранения пакета, выбираем из комбо "Имя пакета" интересующий нас пакет, нажимаем на кнопку "Загрузить пакет в приложение" и в нижнем окне получаем перечень всех соединений внутри пакета. Как происходит загрузка пакета в приложение, мы уже видели из только что приведенного фрагмента кода. Вообще говоря, как мы помним, репозиторные и СОМовские пакеты отличаются не только своими именами, но и версиями внутри одного имени, так что при загрузке необходимо было еще указывать GUID версии. Однако это слишком усложнит наше модельное приложение, поэтому простоты ради будем считать, что: а) внутри каждого пакета находится не более одной версии; б) в одном dts-файле хранится только один пакет. Основным идейным моментом нашего приложения является загрузка списка имен серверных или репозиторных пакетов в комбо. Для перебора локальных пакетов применяется хранимая процедура msdb..sp_enum_dtspackages. Перебор репозиторных пакетов предсталяет собой менее тривиальную задачу, так как подобная процедура для репозитория по понятным причинам отстутствует. Для ее решения я выбрал несколько хакерский путь решения, отследив в SQL Profiler, какие команды поступают на SQL Server, когда мы броузим репозиторий через ММС. После "доработки напильником" родилась следующая "самопальная" хранимая процедура CREATE PROCEDURE sp_enum_dtspkgs_in_repository AS set nocount on create table #MyRTblObjColTemp ( Z_ColNum_Z int NOT NULL DEFAULT 0, IntID binary(8) NOT NULL, Z_BranchID_Z int NOT NULL DEFAULT 0, Z_VS_Z int NOT NULL DEFAULT 0, TypeID binary(8) NOT NULL DEFAULT 0x0000000000000000) Insert into #MyRTblObjColTemp (IntID) select distinct IntID from RTblVersions A, RTblRelships B where B.DstID = 0x13000000CA320000 and B.RelTypeID = 0x000000001A000000 and A.TypeID = B.OrgID update #MyRTblObjColTemp set Z_ColNum_Z = 1, Z_BranchID_Z = A.Z_BranchID_Z, Z_VS_Z = A.Z_VS_Z, TypeID = A.TypeID from RTblVersions A, #MyRTblObjColTemp B where A.IntID = B.IntID and B.Z_ColNum_Z = 0 and A.Z_LClock_Z in (Select Max(Z_LClock_Z) from RTblVersions where IntID = A.IntID) SELECT distinct b.IntID,b.Z_BranchID_Z,b.Z_VS_Z,b.Z_VE_Z,b.Name from RTblVersions a, #MyRTblObjColTemp d, RTblNamedObj b where a.IntID = d.IntID and b.IntID = d.IntID and a.Z_VS_Z >= b.Z_VS_Z and b.Z_VE_Z >= a.Z_VS_Z and d.Z_ColNum_Z = 1 return 0 Процедура была положена в msdb на нескольких серверах и благополучно доказала свою работоспособность, когда Брайан Уэлкер (Brian Welcker) предложил использовать ODBC-шлюз к репозиторию - библиотека Microsoft Repository (\Program Files\Common Files\Microsoft Shared\Repostry\repodbc.dll). Несмотря на то, что в этом случае доступ через ODBC осуществляется несколько медленнее, чем через OLE DB, идея показалась мне более изящной, так как допускает размещение репозитория не только на SQL Server. Кроме того, мы будем использовать в проекте Microsoft ActiveX Data Objects 2.0 Library (d:\Program Files\Common Files\System\ADO\msado20.tlb) и элемент управления Data Bound Combobox из Microsoft DataList Controls 6.0 (OLEDB) (\Winnt4EE\System32\msdatlst.ocx). Для наполнения его списка именами пакета предлагается следующий способ: Const OBJID_IDts TransformationPackage = "{{EBB9995C-BA15-11d1-901B-0000F87A3B33},000032CA}" ..................................................................... Sub EnumDTSPackages() Dim adoRS As ADODB.Recordset Set adoRS = New ADODB.Recordset Select Case lstStorageType.ListIndex Case 0 'На сервере как локальный пакет adoRS.ActiveConnection = "Provider = SQLOLEDB.1; Integrated Security = SSPI;Persist Security Info = False; Initial Catalog = msdb; Data Source = " & txtSrvName.Text adoRS.CursorLocation = adUseClient adoRS.CursorType = adOpenStatic adoRS.Source = "sp_enum_dtspackages" adoRS.Open , , , , adCmdStoredProc Case 1 'Репозиторий Dim Rep As New RepositoryTypeLib.Repository Dim IFaceDef As RepositoryTypeLib.InterfaceDef Dim Pkg As RepositoryTypeLib.RepositoryObject Rep.Open ("SERVER=" & txtSrvName & "; DATABASE = msdb; Integrated Security = SSPI") Set IFaceDef = Rep.object(OBJID_IDtsTransformationPackage) adoRS.Fields.Append "Name", adVarChar, 50 adoRS.Open For Each Pkg In IFaceDef.ObjectInstances adoRS.AddNew "Name", Pkg.Name Next End Select Set cmbPkgName.RowSource = adoRS cmbPkgName.ListField = "name" If adoRS.EOF Then cmbPkgName.Text = "" Else cmbPkgName.Text = adoRS(cmbPkgName.ListField).Value End If End Sub Заметим, что никакого ADO Data Control для нашего DataBound Combo в данном случае не требуется. Генерация множества записей в качестве источника данных происходит для него динамически через объект ADO.Recordset. После того, как пакет загружен, мы получаем доступ из приложения ко всем его свойствам и коллекциям рассматривавшихся нами выше соединений, задач и шагов. Предположим, мы хотим в SQL Server создать прилинкованный сервер для Clipper. Какой провайдер при этом использовать и как его при этом инициализировать, нам неизвестно, но мы знаем, что в состав DTS входит предопределенное соединение с форматом dBase 5, совместимое с файлами CA-Clipper 5.3. Создаем пакет с фиктивным соединением такого типа, и с помощью данного приложения просматриваем коллекцию соединений этого пакета, в частности, свойства ProviderID и Extended Properties. Sub EnumPkgCnns(dtsPkg As dts.Package) Dim dtsCnn As dts.Connection txtPkgCnns.Text = "" For Each dtsCnn In dtsPkg.Connections txtPkgCnns.Text = txtPkgCnns.Text & dtsCnn.ProviderID & vbTab & dtsCnn.DataSource & vbTab & dtsCnn.ConnectionProperties("Extended Properties") & vbNewLine Next End Sub Для соединения dBase 5 c помощью нашего приложения получаем: ProviderID = Microsoft.Jet.OLEDB.4.0; DataSource = D:\TEMP\HetQueries; Extended Properties = dBase 5.0. Отсюда с очевидностью создание прилинкованного сервера для dBase 5 / Clipper: if exists (select srvname from master.dbo.sysservers where srvname='MyDBF') exec sp_dropserver @server='MyDBF', @droplogins='droplogins' go exec sp_addlinkedserver @server='MyDBF', @srvproduct='dBase 5', @provider='Microsoft.Jet.OLEDB.4.0', @datasrc='D:\TEMP\HetQueries', @provstr='dBase 5.0' go После чего можно работать со всеми dbf-таблицами в каталоге D:\TEMP\HetQueries: sp_tables_ex MyDBF go select * from MyDBF...clp_tbl Таким образом мы плавно подошли к нашему третьему вопросу: интеграции службы преобразования данных и гетерогенных запросов. Предполагается, что читатели в той или иной мере знакомы с механизмом гетерогенных запросов. Желающие могут обратиться, в частности, к журналу "СУБД", ©3 за 1998 г., статья называется "Процессор запросов SQL Server 7.0. Некоторые стратегии оптимизатора при построении сложных, параллельных и распределенных планов". С помощью OLE DB-провайдера DTSPackageDSO пакет DTS или отдельный шаг внутри пакета может выступать в качестве источника данных по отношению к внешнему потребителю. Указанный провайдер на самом деле является оберткой, которая запускает пакет (dtsrun в режиме in-process) и возвращает полученное на выходе заданного шага множество записей. Все вызовы, которые потребитель делает к IRowset провайдера тот на самом деле тут же переправляет к множеству записей, полученному от пакета, т.е. играет роль proxy. Для того, чтобы шаг возвращал наружу результаты своей деятельности, нужно присвоить Step. IsPackageDSORowset=true. В DTS Designer это делается с помощью контекстного меню задачи шага -> Workflow Properties -> закладка Options -> отметить DSO Rowset Provider. При этом во время обычного выполнения пакета данный шаг перестает срабатывать, т.е. он переносит в назначение 0 записей. Если требуется, чтобы шаг срабатывал и в то же время служил как источник данных для внешних потребителей, можно создать его копию с IsPackageDSORowset=false на параллельной ветви. Множество записей из пакета можно получить через интерфейсы IOpenRowset и ICommand. Первый способен возвращать только одно множество записей. Если шаг указан, он трактуется как таблица, если нет, пакет выполняется до первого шага, возвращающего результат наружу. Если несколько шагов пакета имеют IsPackageDSORowset=true, можно воспользоваться ICommand и вернуть в приложение сразу несколько множеств записей за один раз, к которым можно обращаться через IMultipleResults::GetResult. Пример создания прилинкованного сервера для DTS-пакета: exec sp_addlinkedserver @server='MyDTS', @srvproduct='PDSO', @provider='DTSPackageDSO', @datasrc='/E /S /NDemoPack: SQL->dbf' Обратим внимание, что для источника данных употребляется строка, аналогичная той, которая применяется в утилите dtsrun. В данном примере мы создали прилинкованный сервер из пакета с именем DemoPack: SQL->dbf, находящийся на локальном сервере (пустая строка после ключа /S), на который мы ходим под безопасностью Windows NT (ключ /E). Пакет совершает перенос таблицы pubs..employee из SQL Server в dBase 5. В опциях задачи переноса (Workflow Properties) отмечено DSO Rowset Provider. До этого мы убедились в том, что пакет действительно работает. Тогда select * from openquery(MyDTS, 'select *') дает клиенту в точности то множество записей, которое должна получить таблица назначения. Аналогичный результат можно получить без предварительного создания прилинкованного сервера: SELECT * FROM OPENROWSET('DTSPackageDSO', '/E /S /NDemoPack: SQL->dbf', 'Select *') Схожим образом осуществляется работа с выделенной версией того или иного пакета. Перенесем наш пакет в репозиторий (Package->Save As, Location=SQL Server Repository), посмотрим GUID версии (папка Data Transformation Services в ММС -> Metadata -> закладка Package в правой части экрана -> раскрыть пакет с нужным названием -> кликнуть на версию за интересующую дату) и перепишем предыдущий запрос так: SELECT * FROM OPENROWSET('DTSPackageDSO', '/E /S /R /NDemoPack: SQL->dbf /V {B50FA41D-DEF5-11D2-BECD-C05A4A000000}', 'Select *') Представим себе, что каждое преобразование в пакете, показанном на рис.6, помечено как DSO Rowset Provider, нас же интересует результат только одного из них. Загрузим пакет из файла в DTS Designer, выбрав версию за нужную нам дату. В свойствах пакета посмотрим Version GUID. В Workflow Properties найдем имя интересующего шага. Окончательный запрос может выглядеть так: SELECT * FROM OPENROWSET('DTSPackageDSO', '/FD:\Temp\Olap\NorthwindMart_Fill.dts /V{10E05829-C260-11D2-BE93-0020AFB67169}', 'Select * from DTSStep_DTSDataPumpTask_4'), где /V характеризует версию пакета, а DTSStep_DTSDataPumpTask_4 - имя
шага.
Об авторе:
Алексей Шуленин, | ||||||||||
За содержание страницы отвечает Гончарова М.Н. © Кафедра СПиКБ, 2002-2017 |