Заполните сводную таблицу из поля с разделителями-запятыми в задаче потока данных в SSIS - PullRequest
1 голос
/ 14 декабря 2010

Это мой 1-й пост, так что будьте нежнее :). Я создаю пакет служб SSIS для сбора информации о работах, выполняемых по всему предприятию на работе. Цель состоит в том, чтобы таблицы заполнялись информацией о серверах, экземплярах, базах данных, заданиях и их отношениях друг с другом. В идеале я буду использовать команды слияния для обновления или вставки, где это необходимо, но сейчас он просто вставляет. Вот краткий обзор моей посылки (пожалуйста, держитесь подальше от водосточной трубы: P):

(ограничения на гиперссылки для новых пользователей означают, что вам нужно заменить знаки + на «t» в моих ссылках

http://img262.imageshack.us/i/package.jpg/

Первая задача «Поток данных» извлекает список экземпляров для проверки из простого текстового файла и сохраняет их в наборе записей. Затем цикл foreach проходит через каждый из них и соответствующим образом изменяет строку подключения моего исходного диспетчера подключений. Внутри цикла мы проходим 1 экземпляр за раз. Задача «Поток данных сервера процессов» используется для поиска имени сервера и добавления его в целевую БД, если оно не существует, в любом случае оно также сохраняет идентификатор и имя сервера в переменных пакета. «Экземпляр процесса» выполняет те же действия, что и выше, но вместо этого. Затем задача «Сбор данных БД» использует эти переменные пакета для вставки всех БД в этом экземпляре в качестве записей с переменными пакета, упомянутыми выше для внешних ключей. Как только это будет завершено, мы переходим к задаче «Сбор данных о заданиях» (надеюсь, это последнее задание для этого пакета). Ниже приводится содержание последнего задания:

http://img809.imageshack.us/i/dataflow.png/

Итак, внутри этой задачи это то, что я делаю до сих пор. Я использую запрос для сбора информации о задании с данными плана обслуживания 1-й. Вот запрос источника OLE DB:

--WRITTEN BY MAXWELL WALLACE
--THE PURPOSE OF THIS QUERY IS TO COLLECT INFORMATION ABOUT JOBS AND MAINTENANCE PLANS
--RUNNING ON A PARTICULAR INSTANCE. IT COLLECTS NAMES, STATUSES, RUN TIMES AND DATES
--AS WELL AS DATABASES AFFECTED AND MAINTENANCE PLAN NAMES IF APPLICABLE.
SELECT B.NAME AS JOB_NAME, B.CATEGORY_ID, 
--RUN_STATUS CODE GETS TRANSLATED INTO ENGLISH
CASE A.RUN_STATUS
WHEN 0 THEN 'Failed'
WHEN 1 THEN 'Succeeded'
WHEN 2 THEN 'Retry'
WHEN 3 THEN 'Canceled'
ELSE 'Unknown'
END AS RUN_STATUS,
--CONVERT INTEGER DATE INTO SOMETHING MORE LEGABLE
SUBSTRING(CAST(A.RUN_DATE AS CHAR(8)),5,2) + '/' + 
RIGHT(CAST(A.RUN_DATE AS CHAR(8)),2) + '/' + 
LEFT(CAST(A.RUN_DATE AS CHAR(8)),4) AS RUN_DATE,
--CONVERT RUN_TIME INTO SOMETHING MORE RECONGNIZABLE (HH:MM:SS)
LEFT(RIGHT('000000' + CAST(A.RUN_TIME AS VARCHAR(10)),6),2) + ':' + 
SUBSTRING(RIGHT('000000' + CAST(A.RUN_TIME AS VARCHAR(10)),6),3,2) + ':' + 
RIGHT(RIGHT('000000' + CAST(A.RUN_TIME AS VARCHAR(10)),6),2) AS RUN_TIME,
--CONVERT RUN_DURATION INTO SOMETHING MORE RECONGNIZABLE (HH:MM:SS)
LEFT(RIGHT('000000' + CAST(A.RUN_DURATION AS VARCHAR(10)),6),2) + ':' + 
SUBSTRING(RIGHT('000000' + CAST(A.RUN_DURATION AS VARCHAR(10)),6),3,2) + ':' + 
RIGHT(RIGHT('000000' + CAST(A.RUN_DURATION AS VARCHAR(10)),6),2) AS RUN_DURATION,
--THE FOLLOWING SUBQUERY IS USED TO EXTRAPOLATE DETAILS FOR THE JOB IN IT'S MAINTENANCE PLAN (IF IT HAS 1)
--THE TOP 1 MAKES SURE WE GET ONLY 1 RECORD SINCE THIS IS A 1 TO MANY RELATIONSHIP
--THE LINE3 COLUMN CONTAINS DETAILS ABOUT THE TASK THAT WAS RUN
(SELECT TOP 1 E.LINE3 
    --WE START WITH THE SYSMAINTPLAN_LOG BECAUSE WE CAN (IN A WAY) JOIN IT TO OUR OUTER JOIN THROUGH THE PLAN_ID IN THE WHERE CLAUSE
    FROM MSDB.DBO.SYSMAINTPLAN_LOG AS D 
    --NOW IT IS POSSIBLE TO, BY EXTENTION, JOIN SYSMAINTPLAN_LOGDETAIL TO THE OUTER JOIN AS WELL THROUGH ITS 1 TO 1 RELATIONSHIP WITH SYSMAINTPLAN_LOG
    INNER JOIN MSDB.DBO.SYSMAINTPLAN_LOGDETAIL AS E ON E.TASK_DETAIL_ID = D.TASK_DETAIL_ID
    --THE 1ST PART OF THE WHERE RETURNS ONLY RECORDS OF THE SAME PLAN_ID, ESSENTIALLY "JOINING" THIS RECORD TO THE OUTER JOIN THE IN MAIN QUERY
    --THE 2ND PART MAKES SURE THE FIELD WE ACTUALLY CARE ABOUT CONTAINS MEANINGFUL DATA
    WHERE D.PLAN_ID = C.PLAN_ID AND E.LINE3 != '') AS PLAN_DETAILS, 
--THE FOLLOWING SUBQUERY RETURNS THE NAME OF THE MAINTENANCE PLAN (IF IT HAS 1)
(SELECT F.NAME
    FROM MSDB.DBO.SYSMAINTPLAN_PLANS AS F --THIS IS A SYSTEM GENERATED VIEW
    --LIKE THE ABOVE SUBQUERY, THIS WHERE ESSENTIALLY "JOINS" THIS RECORD TO THE OUTER JOIN IN THE MAIN QUERY
    WHERE F.ID = C.PLAN_ID) AS PLAN_NAME 
FROM MSDB.DBO.SYSJOBHISTORY AS A
INNER JOIN MSDB.DBO.SYSJOBS AS B ON A.JOB_ID = B.JOB_ID
--THIS OUTTER JOIN ATTACHES PLAN_IDS OF MAINTENANCE PLANS TO JOBS THAT HAVE THEM
LEFT OUTER JOIN SYSMAINTPLAN_SUBPLANS AS C ON C.JOB_ID = B.JOB_ID
--ONLY RETURN ENABLED JOBS
WHERE B.[ENABLED] = 1 
--AND ONLY JOB OUTCOMES, NOT EACH STEP
AND A.STEP_ID = 0 
--AND ONLY COMPLETED JOBS
AND A.RUN_STATUS <> 4
--SORTED BY LATEST DATE 1ST
ORDER BY A.RUN_DATE DESC

Извините, но по какой-то причине этот форум не сохраняет мое форматирование. В любом случае, после этого я добавляю переменную пакета ID экземпляра в качестве столбца, чтобы помочь вставить эти записи с этим внешним ключом. Я преобразую некоторые строки в Unicode, который не существует ни там, ни там, а затем делаю условное разбиение записей с планом обслуживания и без записей. Для записей без я могу просто вставить их в место назначения, и они сделаны! Однако для записей с планом обслуживания вероятность их подключения к одной или нескольким БД очень высока. Итак, сначала я вставляю запись задания в таблицу заданий (точно так же, как я делаю записи, не входящие в план обслуживания), а затем выполняю поиск, чтобы найти идентификатор только что вставленной записи. Затем я извлекаю поле из моего запроса, в котором есть список разделенных запятыми списков БД, на которые распространяется план обслуживания, частью которого является это задание, и разделяю его на VB.Net ArrayList. Затем я назначаю этот ArrayList переменной пакета.

Это та часть, в которой я нахожусь. Очевидно, что мой следующий шаг - создать какой-то цикл с использованием идентификатора задания, который я только что посмотрел и прошел по каждой переменной в ArrayList, чтобы вставить их по одной за раз в таблицу DB / Job Pivot. Проблема в том, что я не знаю, как я могу сделать цикл в Задаче потока данных, и я не могу придумать хороший способ вывести вставку сводной таблицы из этой задачи. Возможно, я смогу сделать это с помощью компонента сценария, но я не уверен, как выполнить предварительную вставку из задачи сценария (должен ли я это учитывать?). Я хорошо знаю VB.Net и C #, а также TSQL, поэтому я могу исследовать любой способ реализации этого. Заранее спасибо за помощь. Ура!

PS. Вот структура таблицы, в которую я вставляю данные:

CREATE TABLE TBL_SERVERS(
ID INT UNIQUE IDENTITY(1,1),
TITLE NVARCHAR(50) PRIMARY KEY,
CLUSTER_NAME NVARCHAR(50) DEFAULT '',
RESOURCES_USED NVARCHAR(20) DEFAULT '',
RESOURCE_THRESHOLD NVARCHAR(20) DEFAULT '',
IS_CLUSTERED BIT NOT NULL DEFAULT 0)

CREATE TABLE TBL_INSTANCES(
ID INT UNIQUE IDENTITY(1,1),
SERVER_ID INT NOT NULL REFERENCES TBL_SERVERS(ID),
TITLE NVARCHAR(50) NOT NULL,
PRIMARY KEY (SERVER_ID,TITLE))

CREATE TABLE TBL_CATEGORY_TYPES(
ID INT UNIQUE IDENTITY(1,1),
TITLE NVARCHAR(50) PRIMARY KEY)

INSERT INTO TBL_CATEGORY_TYPES VALUES ('LOCAL')
INSERT INTO TBL_CATEGORY_TYPES VALUES ('MULTISERVER')
INSERT INTO TBL_CATEGORY_TYPES VALUES ('NONE')

CREATE TABLE TBL_CATEGORY_CLASSES(
ID INT UNIQUE IDENTITY(1,1),
TITLE NVARCHAR(50) PRIMARY KEY)

INSERT INTO TBL_CATEGORY_CLASSES VALUES ('JOB')
INSERT INTO TBL_CATEGORY_CLASSES VALUES ('ALERT')
INSERT INTO TBL_CATEGORY_CLASSES VALUES ('OPERATOR')

CREATE TABLE TBL_CATEGORIES(
ID INT UNIQUE IDENTITY(1,1),
TITLE NVARCHAR(50) NOT NULL,
CATEGORY_CLASS_ID INT NOT NULL REFERENCES TBL_CATEGORY_CLASSES(ID),
CATEGORY_TYPE_ID INT NOT NULL REFERENCES TBL_CATEGORY_TYPES(ID),
PRIMARY KEY (TITLE,CATEGORY_CLASS_ID))

CREATE TABLE TBL_SQL_JOBS(
ID INT PRIMARY KEY IDENTITY(1,1),
TITLE NVARCHAR(200) NOT NULL,
INSTANCE_ID INT NOT NULL REFERENCES TBL_INSTANCES(ID),
CATEGORY_ID INT NOT NULL REFERENCES TBL_CATEGORIES(ID),
RUN_STATUS NVARCHAR(10) NOT NULL,
RUN_DATE NVARCHAR(10) NOT NULL,
RUN_TIME NVARCHAR(8) NOT NULL,
RUN_DURATION NVARCHAR(8) NOT NULL,
MAINTENANCE_PLAN_NAME NVARCHAR(200),
RUN_INTERVAL NVARCHAR(20) DEFAULT '',
IS_ENABLED BIT NOT NULL DEFAULT 1)

SET IDENTITY_INSERT TBL_CATEGORIES ON
INSERT INTO TBL_CATEGORIES (ID,TITLE,CATEGORY_CLASS_ID,CATEGORY_TYPE_ID,CATEGORY_GROUP_ID) VALUES (0,'[Uncategorized (Local)]',1,1,1)
INSERT INTO TBL_CATEGORIES (ID,TITLE,CATEGORY_CLASS_ID,CATEGORY_TYPE_ID,CATEGORY_GROUP_ID) VALUES (2,'[Uncategorized (Multi-Server)]',1,2,1)
INSERT INTO TBL_CATEGORIES (ID,TITLE,CATEGORY_CLASS_ID,CATEGORY_TYPE_ID,CATEGORY_GROUP_ID) VALUES (98,'[Uncategorized]',2,3,1)
INSERT INTO TBL_CATEGORIES (ID,TITLE,CATEGORY_CLASS_ID,CATEGORY_TYPE_ID,CATEGORY_GROUP_ID) VALUES (99,'[Uncategorized]',3,3,1)
INSERT INTO TBL_CATEGORIES (ID,TITLE,CATEGORY_CLASS_ID,CATEGORY_TYPE_ID,CATEGORY_GROUP_ID) VALUES (8,'Data Collector',1,1,1)
INSERT INTO TBL_CATEGORIES (ID,TITLE,CATEGORY_CLASS_ID,CATEGORY_TYPE_ID,CATEGORY_GROUP_ID) VALUES (7,'Database Engine Tuning Advisor',1,1,1)
INSERT INTO TBL_CATEGORIES (ID,TITLE,CATEGORY_CLASS_ID,CATEGORY_TYPE_ID,CATEGORY_GROUP_ID) VALUES (3,'Database Maintenance',1,1,1)
INSERT INTO TBL_CATEGORIES (ID,TITLE,CATEGORY_CLASS_ID,CATEGORY_TYPE_ID,CATEGORY_GROUP_ID) VALUES (5,'Full-Text',1,1,1)
INSERT INTO TBL_CATEGORIES (ID,TITLE,CATEGORY_CLASS_ID,CATEGORY_TYPE_ID,CATEGORY_GROUP_ID) VALUES (1,'Jobs from MSX',1,1,1)
INSERT INTO TBL_CATEGORIES (ID,TITLE,CATEGORY_CLASS_ID,CATEGORY_TYPE_ID,CATEGORY_GROUP_ID) VALUES (6,'Log Shipping',1,1,1)
INSERT INTO TBL_CATEGORIES (ID,TITLE,CATEGORY_CLASS_ID,CATEGORY_TYPE_ID,CATEGORY_GROUP_ID) VALUES (18,'REPL-Alert Response',1,1,1)
INSERT INTO TBL_CATEGORIES (ID,TITLE,CATEGORY_CLASS_ID,CATEGORY_TYPE_ID,CATEGORY_GROUP_ID) VALUES (16,'REPL-Checkup',1,1,1)
INSERT INTO TBL_CATEGORIES (ID,TITLE,CATEGORY_CLASS_ID,CATEGORY_TYPE_ID,CATEGORY_GROUP_ID) VALUES (10,'REPL-Distribution',1,1,1)
INSERT INTO TBL_CATEGORIES (ID,TITLE,CATEGORY_CLASS_ID,CATEGORY_TYPE_ID,CATEGORY_GROUP_ID) VALUES (11,'REPL-Distribution Cleanup',1,1,1)
INSERT INTO TBL_CATEGORIES (ID,TITLE,CATEGORY_CLASS_ID,CATEGORY_TYPE_ID,CATEGORY_GROUP_ID) VALUES (12,'REPL-History Cleanup',1,1,1)
INSERT INTO TBL_CATEGORIES (ID,TITLE,CATEGORY_CLASS_ID,CATEGORY_TYPE_ID,CATEGORY_GROUP_ID) VALUES (20,'Replication',2,3,1)
INSERT INTO TBL_CATEGORIES (ID,TITLE,CATEGORY_CLASS_ID,CATEGORY_TYPE_ID,CATEGORY_GROUP_ID) VALUES (13,'REPL-LogReader',1,1,1)
INSERT INTO TBL_CATEGORIES (ID,TITLE,CATEGORY_CLASS_ID,CATEGORY_TYPE_ID,CATEGORY_GROUP_ID) VALUES (14,'REPL-Merge',1,1,1)
INSERT INTO TBL_CATEGORIES (ID,TITLE,CATEGORY_CLASS_ID,CATEGORY_TYPE_ID,CATEGORY_GROUP_ID) VALUES (19,'REPL-QueueReader',1,1,1)
INSERT INTO TBL_CATEGORIES (ID,TITLE,CATEGORY_CLASS_ID,CATEGORY_TYPE_ID,CATEGORY_GROUP_ID) VALUES (15,'REPL-Snapshot',1,1,1)
INSERT INTO TBL_CATEGORIES (ID,TITLE,CATEGORY_CLASS_ID,CATEGORY_TYPE_ID,CATEGORY_GROUP_ID) VALUES (17,'REPL-Subscription Cleanup',1,1,1)
SET IDENTITY_INSERT TBL_CATEGORIES OFF

CREATE TABLE TBL_APPLICATIONS(
ID INT UNIQUE IDENTITY(1,1),
TITLE NVARCHAR(200) NOT NULL,
HUB_SITE NVARCHAR(50) DEFAULT '',
PRIMARY KEY (TITLE,HUB_SITE))

CREATE TABLE TBL_DATABASES(
ID INT UNIQUE IDENTITY(1,1),
INSTANCE_ID INT NOT NULL REFERENCES TBL_INSTANCES(ID),
TITLE NVARCHAR(200) NOT NULL,
APPLICATION_ID INT REFERENCES TBL_APPLICATIONS(ID),
MANAGED BIT NOT NULL DEFAULT 0,
CONNECTIONSTRING NVARCHAR(MAX) NOT NULL DEFAULT '',
RESOURCES_USED NVARCHAR(MAX) NOT NULL DEFAULT '',
RESOURCE_THRESHOLD NVARCHAR(MAX) NOT NULL DEFAULT '',
LAST_SEEN DATETIME NOT NULL DEFAULT GETDATE(),
PRIMARY KEY (INSTANCE_ID,TITLE))

CREATE TABLE TBL_DATABASE_JOBS(
ID INT UNIQUE IDENTITY(1,1),
DATABASE_ID INT NOT NULL REFERENCES TBL_DATABASES(ID),
JOB_ID INT NOT NULL REFERENCES TBL_SQL_JOBS(ID),
PRIMARY KEY (DATABASE_ID,JOB_ID))

А вот некоторые примеры результатов запроса, который я разместил ранее. Имейте в виду, что скрипт можно запускать для любого экземпляра, если вы используете MSDB, поскольку он использует все сгенерированные системой таблицы и представления:

http://img253.imageshack.us/i/resultsn.jpg/

Просто будьте ясны в отношении моих целей для этого пакета.JOB_NAME, CATEGORY_ID, RUN_DATE, RUN_TIME, RUN_DURATION и PLAN_NAME входят в таблицу TBL_SQL_JOBS.Столбец PLAN_DETAILS ничего не будет делать для пустых значений (как и для PLAN_NAME), но для заполненных записей он удалит строку «Базы данных:» и разделит имена баз данных, разделенные запятыми.Затем необходимо проверить имена БД из разделения по таблице TBL_Databases (ранее заполненной) и получить соответствующий идентификатор.Затем, в сочетании с идентификатором текущей записи задания, которую мы обрабатываем (представьте, что «идентификатор задания поиска» является частью последней задачи пакета), мы добавляем эти записи в таблицу TBL_DATABASE_JOBS отдельно.Конечным результатом является таблица со списком уникальных БД и таблица со списком информации об историческом задании и таблица между ними, которая обеспечивает отношение 1 задание: ко многим БД.Еще раз спасибо.

1 Ответ

0 голосов
/ 14 декабря 2010

Похоже, ваше решение сценария это путь. И вам не нужно беспокоиться о вставке из скрипта.

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

Важным шагом является установка асинхронного компонента скрипта. Асинхронный режим с включением компонента для вывода другого числа строк, чем введенное количество строк. После создания нового компонента сценария в качестве Transformation отредактируйте компонент. Выберите Свойства ввода и вывода, выберите Вывод 0. Измените значение свойства SynchronousInputID на Нет. Разверните ветку Output 0 и добавьте столбцы в ветку Output Columns. Столбцы будут содержать строки, которые выходят из компонента. Наконец, добавьте свой код скрипта, который создаст строку для каждого элемента в вашем списке через запятую.

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

...