![](https://habrastorage.org/web/128/5b7/4dc/1285b74dc03b4bd48c84d1894b2c5812.png)
> Часть 1
> Часть 2
В этой части я расскажу о работе с параметрами и переменными внутри SSIS-пакета. Узнаем, как можно задавать и отслеживать значения переменных во время выполнения пакета.
Также рассмотрим вызов одного пакета из другого при помощи «Execute Package Task» и некоторые дополнительные компоненты и решения.
Здесь тоже будет много картинок.
Продолжим знакомство с SSIS
Создадим в трех демонстрационных базах новую таблицу ProductResidues, которая будет содержать информацию об остатках на каждый день:
USE DemoSSIS_SourceA
GO
CREATE TABLE ProductResidues(
ResidueDate date NOT NULL,
ProductID int NOT NULL,
ResidueAmount decimal(10,2) NOT NULL,
CONSTRAINT PK_ProductResidues PRIMARY KEY(ResidueDate,ProductID),
CONSTRAINT FK_ProductResidues_ProductID FOREIGN KEY(ProductID) REFERENCES Products(ID)
)
GO
USE DemoSSIS_SourceB
GO
CREATE TABLE ProductResidues(
ResidueDate date NOT NULL,
ProductID int NOT NULL,
ResidueAmount decimal(10,2) NOT NULL,
CONSTRAINT PK_ProductResidues PRIMARY KEY(ResidueDate,ProductID),
CONSTRAINT FK_ProductResidues_ProductID FOREIGN KEY(ProductID) REFERENCES Products(ID)
)
GO
USE DemoSSIS_Target
GO
CREATE TABLE ProductResidues(
ResidueDate date NOT NULL,
ProductID int NOT NULL,
ResidueAmount decimal(10,2) NOT NULL,
CONSTRAINT PK_ProductResidues PRIMARY KEY(ResidueDate,ProductID),
CONSTRAINT FK_ProductResidues_ProductID FOREIGN KEY(ProductID) REFERENCES Products(ID)
)
GO
И наполним таблицы в источниках тестовыми данными, поочередно выполнив на базах DemoSSIS_SourceA и DemoSSIS_SourceB следующий скрипт:
USE DemoSSIS_SourceA
--USE DemoSSIS_SourceB
GO
DECLARE @MinDate date=DATEADD(MONTH,-2,GETDATE())
DECLARE @MaxDate date=GETDATE()
;WITH dayCTE AS(
SELECT CAST(@MinDate AS date) ResidueDate,10000 ResidueAmount
UNION ALL
SELECT DATEADD(DAY,1,ResidueDate),ResidueAmount-1
FROM dayCTE
WHERE ResidueDate<@MaxDate
)
INSERT ProductResidues(ResidueDate,ProductID,ResidueAmount)
SELECT
d.ResidueDate,
p.ID,
d.ResidueAmount
FROM dayCTE d
CROSS JOIN Products p
OPTION(MAXRECURSION 0)
Допустим, что в таблице ProductResidues будет очень много строк, и чтобы каждый раз не перезагружать всю информацию и упростить процедуру интеграции, логику загрузки в таблицу ProductResidues в базе DemoSSIS_Target реализуем следующую:
- Если в принимающей БД еще нет записей, то загрузим все данные;
- Если в принимающей БД есть данные, то будем удалять данные за неделю (последние 7 дней) от последней загруженной даты и загружать данные из источника начиная от этой даты снова.
Неудобство интеграции таблиц типа ProductResidues в том, что в ней нет полей, по которым можно было бы однозначно определить, когда появилась запись, в ней нет никакого идентификатора типа ID, нет ни поля типа UpdatedOn, которое содержало бы дату/время последнего обновления записи (т.е. зацепиться особо не за что), иначе бы мы, например, могли вычислить для каждого источника, по данным базы DemoSSIS_Target, последний ID или последнюю UpdatedOn и уже загружать данные из источников начиная от этих стартовых значений. К тому же не всегда есть возможность внести изменения в структуру источников, подстроив их под себя, т.к. это могут быть вообще чужие базы, к которым у нас нет полного доступа.
В нашем же случае еще допустим, что пользователи могут менять данные задним числом и могут вообще удалить некоторые ранее загруженные в базу DemoSSIS_Target строки из источников. Поэтому здесь обновление делается как-бы внахлёст, данные последней недели полностью перезаписываются. Здесь неделя берется условно, об этом минимальном сроке, например, мы могли бы условиться с заказчиком (он мог подтвердить, что данные обычно меняются максимум в течении недели). Конечно это не самый надежный способ, и иногда могут возникнуть расхождения, например, в том случае, когда пользователь поменял данные месячной давности и здесь стоит предусмотреть возможность перезагрузки данных начиная с более ранней даты, мы сделаем это при помощи параметра, в котором будем указывать нужное нам количество дней назад.
Создадим новый SSIS-пакет и назовем его «LoadResidues.dtsx».
При помощи контекстного меню, отобразим область ввода переменных пакета (это можно сделать также при помощи меню «SSIS > Variables»):
![](https://habrastorage.org/web/257/437/f46/257437f4656c4768a0b4e2935000f883.png)
В данном пакете, нажав на кнопку «Add Variable», создадим переменную LoadFromDate типа DateTime:
![](https://habrastorage.org/web/b0b/ba1/05a/b0bba105a4d549bd8e99df5c3e6b9105.png)
По умолчанию переменной присваивается текущее значение даты/времени, т.к. мы будем переопределять значение внутри пакета, нам это не важно.
Так же стоит обратить внимание на Expression – если прописать в этом поле выражение, то переменная будет работать как формула и мы не сможем изменять ее значение при помощи присваивания. При каждом обращении к такой переменной ее значение будет рассчитываться согласно указанному выражению.
Значения переменных можно задавать при помощи компонента «Expression Task». Давайте рассмотрим, как это делается. Создадим элемент «Expression Task»:
![](https://habrastorage.org/web/582/8f4/9c5/5828f49c572e443d80ddb076e20d8cf9.png)
Двойным щелчком откроем редактор данного элемента:
![](https://habrastorage.org/web/2ee/84c/c1e/2ee84cc1ec4f40628a8214813cbb62a6.png)
Пропишем следующее выражение:
@[User::LoadFromDate] = (DT_DATE) (DT_DBDATE) GETDATE()
Здесь так же была применена двойная конвертация типа, чтобы избавиться от составляющей времени и оставить только дату.
Давайте так же посмотрим как можно отследить значение переменной во время выполнения пакета.
Создадим точку останова на элементе «Expression Task»:
![](https://habrastorage.org/web/362/d22/909/362d229099e541e0a56dbd4a3e253b98.png)
Укажем, что точка должна срабатывать по окончанию выполнения данного блока:
![](https://habrastorage.org/web/4ee/6a9/fad/4ee6a9fada7b4ba3bca58cfd331f0112.png)
Запустим пакет на выполнение (F5) и после остановке в нашей точке, перейдем на вкладку «Locals»:
![](https://habrastorage.org/web/9ca/bca/3c0/9cabca3c002947c392cafa683a7da68b.png)
Раскроем список Variables и найдем в нем свою переменную:
![](https://habrastorage.org/web/8a3/a69/30c/8a3a6930cae147c3841e0a86e23312fc.png)
Для интереса можем поменять выражение элементе «Expression Task» на следующее:
@[User::LoadFromDate] = (DT_DATE) (DT_DBDATE) DATEADD("DAY", -7, GETDATE())
и также поэкспериментировать:
![](https://habrastorage.org/web/7fa/656/9cd/7fa6569cd69f4167a7c23a495b529af0.png)
Точку останова убирается таким же образом каким и была установлена. Либо можно удалить сразу все точки останова, если их было несколько:
![](https://habrastorage.org/web/40f/560/351/40f56035194e4b9396daefd36f335a4e.png)
Создадим параметр, который будет отвечать за количество дней назад.
Параметр можно создать, как глобальный для всего проекта:
![](https://habrastorage.org/web/fd8/5c7/d66/fd85c7d664014284915d5053bac3d438.png)
Так и локальный, внутри конкретного пакета:
![](https://habrastorage.org/web/cbc/47b/451/cbc47b451e57432cb40ae8449a007b5c.png)
Параметр может быть обязательный для задания – за это отвечает флаг Required. Если этот флаг установлен, то при создании задачи или при вызове пакета из другого пакета нужно будет определить входящее значение параметра (мы рассмотрим это далее).
В отличие от переменных значение параметров при помощи «Expression Task» менять нельзя.
Сохраним параметры и снова зайдем в редактор «Expression Task»:
![](https://habrastorage.org/web/591/e2a/741/591e2a741012455697b2d18d44712897.png)
Для примера я поменял выражение на следующее:
@[User::LoadFromDate] = (DT_DATE) (DT_DBDATE) DATEADD("DAY", - @[$Package::DateOffset] , GETDATE())
Думаю, на этом суть параметров и переменных ясна, и мы можем продолжить.
После того как мы поигрались с «Expression Task» мы его удалим.
Создадим «Execute SQL Task»:
![](https://habrastorage.org/web/6db/53a/e73/6db53ae73ccf424483570c0d6869cb30.png)
Настроим его следующим образом:
![](https://habrastorage.org/web/ae8/d83/618/ae8d83618bb34952b00d308dc436f09e.png)
Пропишем в SQLStatement следующий запрос:
SELECT ISNULL(DATEADD(DAY,-?,MAX(ResidueDate)),'19000101') FromDate
FROM ProductResidues
Т.к. данный запрос возвращает одну строку, установим ResultSet = «Single Row» и ниже на вкладке «Result Set» сохраним результат в значение переменной LoadFromDate.
На вкладке «Parameter Mapping» зададим значения параметров, которые в запросе обозначены знаком вопроса (?):
![](https://habrastorage.org/web/f12/c31/c77/f12c31c77c8f4298a65ed6ff96dbd6d8.png)
Параметры нумеруются, начиная с нуля.
Стоит отметить, что если при создании соединения воспользоваться другим видом провайдера, например, «ADO» или «ADO.Net», то вместо вопросов мы сможем использовать именованные параметры типа @ParamName и в качестве «Parameter Name» тоже могли бы указывать @ParamName, а не его номер. Но увы типом соединения с другим провайдером мы сможем воспользоваться не во всех случаях.
Теперь на вкладке «Result Set» укажем в какую переменную нужно записать результат выполнения запроса:
![](https://habrastorage.org/web/c3c/f20/39a/c3cf2039a1e343fab1d83d7728053e36.png)
Здесь так же можем продебажить установив у этого компонента точку останова на «Break when the container receives the OnPostExecute event» и запустив пакет на выполнение:
![](https://habrastorage.org/web/b9a/b48/c9c/b9ab48c9c0844969a6ae52a4e618dd15.png)
Здесь я для удобства мониторинга значения переменной прописал название переменной в «Watch», чтобы не искать ее в блоке «Locals».
Как видим все верно в переменную LoadFromDate записалась дата «01.01.1900», т.к. строк в таблице ProductResidues на Target еще нет.
Переименую для наглядности «Execute SQL Task» в «Set LoadFromDate».
Создадим еще один элемент «Execute SQL Task» и назовем его «Delete Old Rows»:
![](https://habrastorage.org/web/cde/6ef/667/cde6ef667cd7428facd2f16225932093.png)
Настроим его следующим образом:
![](https://habrastorage.org/web/e1a/6c2/32b/e1a6c232b957465b8617d4998207a8bf.png)
SQLStatement содержит следующий запрос:
DELETE ProductResidues
WHERE ResidueDate>=?
И зададим значение параметра на вкладке «Parameters Mapping»:
![](https://habrastorage.org/web/e31/3f6/708/e313f6708fda49c1901df588345d5d5e.png)
Все, удаление старых данных за указанный конечный период у нас реализовано.
Теперь сделаем часть отвечающую загрузку свежих данных. Для этого воспользуемся компонентом «Data Flow Task»:
![](https://habrastorage.org/web/a8a/c58/05f/a8ac5805fbd74615a9381d5aceb5435a.png)
Зайдем в область данного компонента и создадим «Source Assistant»:
![](https://habrastorage.org/web/868/40a/0cd/86840a0cd37747588307b3f842c79f35.png)
Настроим его следующим образом:
![](https://habrastorage.org/web/c1d/646/efe/c1d646efeacd47188dccac560c15f8fd.png)
Нажав на кнопку «Parameters…» зададим значение параметра:
![](https://habrastorage.org/web/848/139/327/84813932749b4e54b12c14ef10133291.png)
Для записи новых данных воспользуемся уже знакомым компонентом «Destination Assistant»:
![](https://habrastorage.org/web/aab/54d/884/aab54d8845fc49d4804133f928dac051.png)
Протянем стрелку от «Source Assistant» и настроим его:
![](https://habrastorage.org/web/d11/5e3/fdf/d115e3fdf43f4bd4816ca4e89fe10cf8.png)
![](https://habrastorage.org/web/93e/a9c/005/93ea9c005ec24ad6a883f3b12cfbfaea.png)
![](https://habrastorage.org/web/5be/ee3/2f9/5beee32f91a34f529cf2e586294942c2.png)
Все, пакет для переноса данных с источника SourceA у нас готов, можем запустить его на выполнение:
![](https://habrastorage.org/web/517/e7f/5d3/517e7f5d3b2b44fd934b417c4697b26b.png)
Запустим еще раз:
![](https://habrastorage.org/web/44a/978/2c7/44a9782c791b4ddbad3f46d2e987b20c.png)
Как видим при повторном запуске удалились и залились только данные за указанный период. Ну вроде все работает так как мы и хотели. На всякий случай сверимся, что данные получателя по количеству равны данным источника:
![](https://habrastorage.org/web/0c5/e7c/99e/0c5e7c99e1ee4caf9b0c7773fa85cb5c.png)
Дойдя до сюда, я понял, что я допустил ошибку. Кто понял в чем дело, молодец!
Но может это и хорошо, т.к. пример получился не таким перегруженным.
Ошибка в том, что я забыл учесть, что при интеграции данных таблицы Products у нас в Target формируются свои идентификаторы (поле ID с флагом IDENTITY)!
Давайте переделаем, чтобы все было правильно. Ничего страшного повторим, зато лучше запомним.
Забежим чуть вперед и добавим в пакет еще один параметр, который назовем «SourceID»:
![](https://habrastorage.org/web/354/21a/332/35421a332f8c4d9d9164392eedf39388.png)
Перенастроим «Set LoadFromDate»:
![](https://habrastorage.org/web/9e8/b85/7fa/9e8b857fa3804379ab42befd154c2e78.png)
В SQLStatement пропишем новый запрос с учетом SourceID:
SELECT ISNULL(DATEADD(DAY,-?,MAX(res.ResidueDate)),'19000101') FromDate
FROM ProductResidues res
JOIN Products prod ON res.ProductID=prod.ID
WHERE prod.SourceID=?
Настроим второй параметр:
![](https://habrastorage.org/web/bd6/7f5/6aa/bd67f56aa35945379c287c5b55faa78d.png)
Теперь перенастроим «Delete Old Rows» аналогичным образом, чтобы учитывался SourceID:
![](https://habrastorage.org/web/0e1/8b3/4ff/0e18b34ffd9f4bf0a0b32fb1a43bd507.png)
В SQLStatement пропишем новый запрос с учетов SourceID:
DELETE res
FROM ProductResidues res
JOIN Products prod ON res.ProductID=prod.ID
WHERE ResidueDate>=?
AND prod.SourceID=?
Настроим второй параметр:
![](https://habrastorage.org/web/a05/5f8/373/a055f83733864fd19a23d0143b9d6789.png)
Теперь зайдем в «Data Flow Task», удалим цепочку и добавим «Derived Column»:
![](https://habrastorage.org/web/a2a/ef1/426/a2aef14262ce4a87a86f2235ffbf6320.png)
Настроим его следующим образом:
![](https://habrastorage.org/web/755/ece/89a/755ece89a85c4a25ac31dd7b1b1423f4.png)
Здесь я намеренно оставил тип «Unicode string», а не сделал преобразование как в первой части. Давайте за одно рассмотрим компонент «Data Conversion»:
![](https://habrastorage.org/web/876/1c9/76e/8761c976e0364d1eb073276abf4f6bc4.png)
Настроим его:
![](https://habrastorage.org/web/823/c9b/3f5/823c9b3f506746108993faa728212c93.png)
Теперь при помощи Lookup сделаем сопоставление и получим нужные нам идентификаторы продуктов:
![](https://habrastorage.org/web/438/6d3/9b5/4386d39b57784430aa36704a3040b2ba.png)
Настроим его:
![](https://habrastorage.org/web/b3a/2c8/66a/b3a2c866af21434c8942fd439f6e6722.png)
![](https://habrastorage.org/web/fd2/543/94d/fd254394dcd74ddcafed42e5a12411f2.png)
![](https://habrastorage.org/web/673/20e/d21/67320ed217a44c539be536a33034e522.png)
Теперь протянем синюю стрелку от Lookup к «OLE DB Destination»:
![](https://habrastorage.org/web/1d8/180/661/1d81806611e64cf1bcfd7d9f1eb15ea3.png)
Выберем поток «Lookup Match Output»:
![](https://habrastorage.org/web/a32/745/8a4/a327458a465f451fbf5c522106f0deb0.png)
Настроим «OLE DB Destination», нужно перестроить Mappings:
![](https://habrastorage.org/web/0c8/dda/dd8/0c8ddadd86634e7ea32c75a43b44595c.png)
Все, сделаем очистку таблицы от неправильно загруженных данных:
TRUNCATE TABLE DemoSSIS_Target.dbo.ProductResidues
И запустим пакет на выполнение:
![](https://habrastorage.org/web/564/464/9d1/5644649d1a4a45bdb5f93d8564f19f89.png)
И еще раз:
![](https://habrastorage.org/web/931/917/849/93191784900b422b9629001d4c9fa4e6.png)
Похоже на правду. Можете самостоятельно проверить правильно ли разнеслись идентификаторы продуктов.
Так как структура DemoSSIS_SourceA и DemoSSIS_SourceB одинакова и нам нужно сделать для DemoSSIS_SourceB, то же самое, то мы можем при создании задачи создать два шага для пакета «LoadResidues.dtsx», в первом шаге настроить подключение к базе DemoSSIS_SourceA, а на втором шаге DemoSSIS_SourceB.
Откомпилируем и передеплоим SSIS проект:
![](https://habrastorage.org/web/ca9/4c8/8be/ca94c88be1bd45589c8b7afeb5595d6f.png)
![](https://habrastorage.org/web/648/086/03b/64808603b7734fe8a54dad1eb2bdec6e.png)
![](https://habrastorage.org/web/a44/806/8f7/a448068f710c4888a194fdc6b73a8c13.png)
Давайте теперь создадим новое задание в SQL Agent:
![](https://habrastorage.org/web/7ba/050/b30/7ba050b303f84572b232a892f73aefb2.png)
На вкладке Steps создадим шаг 1 для загрузки продуктов:
![](https://habrastorage.org/web/e29/5d6/f8b/e295d6f8b53e46cb87bad12311b526ca.png)
Создадим шаг 2 для загрузки остатков из SourceA:
![](https://habrastorage.org/web/2fc/a61/9dd/2fca619ddf464c4bb3dfb6a34215ebcb.png)
На вкладке Configuration можем увидеть наши параметры:
![](https://habrastorage.org/web/d59/aec/a57/d59aeca579224adc9116ec66ec001925.png)
Здесь от нас требуют ввести SourceID, т.к. мы указали его как Required. Зададим его:
![](https://habrastorage.org/web/4b6/b77/604/4b6b77604dd044269fbccaf026f81423.png)
Данные вкладки «Connection Managers» изменять для этого шаге не будем.
Создадим шаг 3 для загрузки остатков из SourceB:
![](https://habrastorage.org/web/e52/92e/96a/e5292e96a8d9406a88d7c99d8ef66ca9.png)
Зададим параметр SourceID:
![](https://habrastorage.org/web/3c9/573/52f/3c957352ffc043ea807ddadb224e333e.png)
И изменим данные соединения SourceA таким образом, чтобы оно ссылалось на базу DemoSSIS_SourceB:
![](https://habrastorage.org/web/e5c/bdb/c5a/e5cbdbc5a6c14f128baa84c3c4e4580f.png)
В данном случае мне достаточно было изменить ConnectionString и InitialCatalog, теперь они указывают на DemoSSIS_SourceB.
В итоге мы должны получить следующее – три шага:
![](https://habrastorage.org/web/469/34c/c43/46934cc43df74aef9db79a44b47edbaf.png)
Запустим эту задачу на выполнение:
![](https://habrastorage.org/web/123/dee/cbf/123deecbf5eb4a47a45a6fe6103b9c95.png)
Выполним запрос:
USE DemoSSIS_Target
GO
SELECT prod.SourceID,COUNT(*)
FROM ProductResidues res
JOIN Products prod ON res.ProductID=prod.ID
GROUP BY prod.SourceID
И убедимся, что все отработало как надо:
![](https://habrastorage.org/web/155/2a6/9d0/1552a69d01094677a23e9b0db9bb1e0b.png)
Теперь допустим, что базы DemoSSIS_SourceA и DemoSSIS_SourceB расположены на одном экземпляре SQL Server. Давайте переделаем «OLE DB Source»:
![](https://habrastorage.org/web/1ed/391/05e/1ed39105ebff412ebbba6bf72f90fc77.png)
![](https://habrastorage.org/web/bbd/42d/be3/bbd42dbe389347a3a3e77fa92b1564fe.png)
Текст команды:
DECLARE @SourceID char(1)=?
IF(@SourceID='A') USE DemoSSIS_SourceA
ELSE USE DemoSSIS_SourceB
SELECT
ResidueDate,
ProductID,
ResidueAmount
FROM ProductResidues
WHERE ResidueDate>=?
Зададим параметры:
![](https://habrastorage.org/web/cc6/8a2/e78/cc68a2e78b4443089b9105e20394e846.png)
Теперь наш пакет в зависимости от значения параметра SourceID будет брать данные либо из SourceA, либо из SourceB.
Для теста можете изменить значение параметра SourceB на «B» и запустить проект на выполнение:
![](https://habrastorage.org/web/4eb/f22/d00/4ebf22d005ea4dfc8c6409b22fff042b.png)
![](https://habrastorage.org/web/a4a/3c6/7d9/a4a3c67d936346edbb754e7a8514a0f0.png)
Давайте теперь создадим новый пакет «LoadAll.dtsx» и создадим в нем «Execute Package Task» (переименуем его в «Load Products»):
![](https://habrastorage.org/web/f6e/722/00d/f6e72200dd6440ed97584c559078b871.png)
Настроим «Load Products»:
![](https://habrastorage.org/web/ea0/3b0/7d8/ea03b07d87cc4c5bb1c6d997eb0fbd27.png)
Создадим в новом пакете 2 параметра:
![](https://habrastorage.org/web/dff/d99/fc5/dffd99fc5fbf4aabaa529a1de1b389f2.png)
В области «Control Flow» создадим еще 2 компонента «Execute Package Task» которые назовем «Load Resudues A» и «Load Resudues B»:
![](https://habrastorage.org/web/4d9/259/a78/4d9259a78151431b9587834a56816ef9.png)
Настроим их задав у обоих название пакета «LoadResidues.dtsx»:
![](https://habrastorage.org/web/c6c/d44/525/c6cd445254c2456e8dcd96c3109095c8.png)
Зададим обязательный параметр SourceID для «Load Resudues A»:
![](https://habrastorage.org/web/949/10d/683/94910d683fe54c3c8b9d83648626058e.png)
Зададим обязательный параметр SourceID для «Load Resudues B»:
![](https://habrastorage.org/web/80c/e7b/763/80ce7b763adc45ae867348a7c2d2691a.png)
Обратите внимание, что у стрелок тоже есть свои свойства, например, мы можем поменять свойство Value на Completion, что будет означать, что следующий шаг будет выполнен даже в том случае если на шаге «Load Resudues A» произойдет ошибка:
![](https://habrastorage.org/web/ff7/54c/4e1/ff754c4e1ebf4d72ad023ea1501f8aaa.png)
Все, можем запустить пакет на выполнение:
![](https://habrastorage.org/web/187/888/9e2/1878889e230d4a75a904b4cc98a22806.png)
Думаю, объяснять, что здесь произошло нет смысла.
Иногда параметры для конкретного пакета удобно хранить в вспомогательной таблице и считывать их оттуда в переменные пакета, например, используя для поиска глобальную переменную «System::PackageName». Для демонстрации, давайте переделаем наш пакет таким образом.
Создадим таблицу с параметрами:
USE DemoSSIS_Target
GO
CREATE TABLE IntegrationPackageParams(
PackageName nvarchar(128) NOT NULL,
DateOffset int NOT NULL,
CONSTRAINT PK_IntegrationPackageParams PRIMARY KEY(PackageName)
)
GO
INSERT IntegrationPackageParams(PackageName,DateOffset)VALUES
(N'LoadResidues',7)
GO
Удалим параметр DateOffset из пакета «LoadResidues.dtsx»:
![](https://habrastorage.org/web/150/e21/15d/150e2115dac64fd4bca6e6ef938cb88e.png)
Создадим в пакете переменную DateOffset:
![](https://habrastorage.org/web/744/ebe/8a9/744ebe8a903f437e933fef0da9cb9eae.png)
В область «Control Flow» добавим еще один элемент «Execute SQL Task» и переименуем его «Load Params»:
![](https://habrastorage.org/web/a09/83e/871/a0983e8712874a1cb8250602086155bb.png)
Настроим его:
![](https://habrastorage.org/web/e79/ae3/ef7/e79ae3ef738c40fa818ad0fff2b95238.png)
Запрос в SQLStatement пропишем следующий:
SELECT DateOffset
FROM IntegrationPackageParams
WHERE PackageName=?
Настроим параметр запроса используя системную переменную «System::PackageName»:
![](https://habrastorage.org/web/d19/a93/662/d19a936621124001aff9645604278d34.png)
Осталось сбросить результат выполнения запроса в переменную:
![](https://habrastorage.org/web/c91/7e3/e90/c917e3e9094e46ceb3c67e22c075b03b.png)
Теперь осталось перенастроить «Set LoadFromDate», чтобы в нем использовалась теперь переменная:
![](https://habrastorage.org/web/7e8/6c2/bf5/7e86c2bf5d6d4ebbba2ddfbd8276cab5.png)
![](https://habrastorage.org/web/515/3b9/755/5153b9755d624056b26cd16074b4d81d.png)
Все, можем тестировать новую версию пакета.
Вот мы и добрались до финиша. Мои поздравления!
Заключение по третьей части
Уважаемые читатели, эта часть будет заключительной.
В данном цикле статей, я постарался продумать примеры таким образом, чтобы сделать их как можно короче и в свою очередь охватить как можно больше полезных и важных деталей.
Думаю, освоив это, далее вы уже без особого труда сможете освоить работу с остальными компонентами SSIS. В данных статьях я рассмотрел только самые важные компоненты (наиболее часто применяемые на моей практике), но зная только это вы уже можно сделать очень многое. По мере надобности изучайте самостоятельно другие компоненты, в первую очередь порекомендовал бы посмотреть следующее:
- компоненты-контейнеры (Sequence Container, For Loop Container, Foreach Loop Container);
- «Merge Join», который позволяет сделать операции JOIN, LEFT JOIN, FULL JOIN на стороне SSIS (это требует предварительно отсортировать два набора при помощи «Sort»);
- «Conditional Split», который позволяет разбить один поток на несколько в зависимости от условий;
- так же рассмотрите вкладку «Event Handlers», которая позволяет создать дополнительные области для определенного вида события пакета.
Доступного материала на эту тему очень много, используйте MSDN, Youtube и прочие источники.
Я не старался сделать подробный учебник (думаю, все это уже есть), а старался сделать такой материал, который позволит начинающим шаг за шагом создать все с самого нуля и делая все своими руками увидеть всю картину в целом, а после уже имея основу идти дальше самостоятельно. Очень надеюсь, что у меня это получилось и материал окажется полезен именно в таком ключе.
Я очень рад, что мне хватило сил осуществить задуманное и описать все так как я это хотел, даже получилось сделать большее, так как из-за допущенной в этой части ошибки возникли неожиданные повороты сюжета, но так я думаю, стало даже интересней. ;)
Спасибо за внимание! Удачи!
И возможно, до встреч в новых статьях…
Поделиться с друзьями
silvercaptain
Спасибо за серию статей.
На самом деле проделана огромная работа
Я не уверен, что мог бы себе позволить вырезать из жизни такой кусок, для написания подобной статьи.
Так держать!
Leran2002
Спасибо за понимание!
Времени ушло действительно немало, и очень надеюсь, что оно было потрачено не зря и материал окажется многим полезен. ;)
Спасибо так же моей семье, которая понимала и поддерживала меня все это время, мирясь с моей «недоступностью» и временами терпя меня такого «противного».