2010-12-14 114 views
1

Ceci est mon premier message, alors soyez gentil :). Je construis un package SSIS pour collecter des informations sur les travaux en cours dans toute l'entreprise au travail. L'objectif est d'avoir des tables remplies d'informations sur les serveurs, les instances, les bases de données, les tâches et leurs relations les unes avec les autres. Idéalement, j'utiliserai les commandes de fusion pour les mettre à jour ou les insérer là où c'est nécessaire, mais pour l'instant, il suffit de les insérer. Voici un aperçu de mon colis (garder votre esprit hors de la gouttière s'il vous plaît: P):Remplissage de tableau croisé dynamique à partir d'un champ délimité par des virgules dans une tâche de flux de données dans SSIS

(restrictions sur les liens pour les nouveaux utilisateurs signifie que vous devez remplacer les signes + avec « t » dans mes liens

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

La 1ère tâche de flux de données tire dans une liste d'instances pour vérifier à partir d'un fichier texte et les stocke dans un La boucle foreach jeu d'enregistrements. passe alors par chacun d'eux et modifie la chaîne de connexion de ma source Connection Manager Dans la boucle, nous passons par une instance à la fois: la tâche de flux de données "Process Server" est utilisée pour trouver le nom du serveur et l'ajouter à la base de données de destination si elle ne l'est pas. existe, de toute façon, il stocke également l'ID du serveur et le nom dans les variables du package. L'instance de processus fait la même chose que ci-dessus mais pour l'instance à la place. La tâche "Collecte de données de base de données" utilise ensuite ces variables de package pour insérer tous les DB dans cette instance en tant qu'enregistrements avec les variables de package mentionnées ci-dessus pour les clés étrangères. Une fois que cela est terminé, nous passons à la tâche "Recueillir les données de travail" (espérons la dernière tâche pour ce paquet). Ce qui suit est le contenu de la dernière tâche:

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

donc à l'intérieur de cette tâche est ce que je fais jusqu'à présent. J'utilise une requête pour collecter les informations sur les tâches avec les données du plan de maintenance 1. Voici la requête pour le OLE DB Source:

--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 

Désolé, mais pour une raison quelconque ce forum ne conserve pas mon formatage. Quoi qu'il en soit, après cela, j'ajoute la variable de package Instance ID en tant que colonne pour aider à insérer ces enregistrements avec cette clé étrangère. Je convertis certaines chaînes en unicode qui n'est ni ici ni là et ensuite je fais une division conditionnelle sur les enregistrements avec un plan de maintenance et des enregistrements sans. Pour les enregistrements sans je peux simplement les insérer dans la destination et ils sont faits! Cependant, pour les enregistrements avec un plan de maintenance, la probabilité qu'ils aient une connexion à une ou plusieurs bases de données est très élevée. Donc, tout d'abord, j'insère l'enregistrement du travail dans la table des tâches (exactement comme je le fais avec les enregistrements ne faisant pas partie d'un plan de maintenance) et ensuite je recherche l'enregistrement que je viens d'insérer. Ensuite, je prends le champ de ma requête qui a une liste de DB séparés par des virgules affectées par le plan de maintenance dont ce travail fait partie et le sépare en une ArrayList VB.Net. J'affecte ensuite cette ArrayList à une variable de package.

C'est la partie où je suis. Évidemment, l'étape suivante consiste à créer une sorte de boucle en utilisant l'ID de travail que je viens de rechercher et de parcourir chaque variable dans ArrayList pour les insérer 1 à la fois dans la table DB/Job Pivot. Le problème est que je ne suis pas au courant de comment je peux faire une boucle dans une tâche de flux de données et je ne peux pas penser à un bon moyen de déplacer l'insertion de la table pivotante de cette tâche. Je pourrais être en mesure de le faire avec un composant de script, mais je ne sais pas comment préformer des inserts à partir de la tâche de script (devrais-je même considérer cela?). Je suis compétent avec VB.Net et C# ainsi que TSQL afin que je puisse étudier toute méthode d'implémentation. Merci d'avance pour votre aide. À votre santé!

PS. Voici la structure de la table j'insérer les données dans:

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)) 

Et voici quelques exemples de résultats de la requête que j'ai posté plus tôt.Gardez à l'esprit peut être exécuté le script sur une instance aussi longtemps que vous avez utilisé MSDB car il utilise tous les systèmes généré tables et vues:

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

Il suffit de faire clairs sur mes objectifs pour ce paquet. JOB_NAME, CATEGORY_ID, RUN_DATE, RUN_TIME, RUN_DURATION et PLAN_NAME vont tous dans la table TBL_SQL_JOBS. La colonne PLAN_DETAILS ne fera rien pour les valeurs nulles (comme PLAN_NAME) mais pour les enregistrements remplis, elle supprimera la chaîne "Databases:" et divisera les noms de base de données délimités par des virgules. Ensuite, il doit vérifier les noms de DB de la division par rapport à la table TBL_Databases (précédemment rempli) et récupérer l'ID correspondant. Ensuite, combiné avec l'ID de l'enregistrement de travail en cours que nous traitons (pensez à la partie "ID de travail de recherche" de la dernière tâche du package), nous ajoutons ces enregistrements à la table TBL_DATABASE_JOBS séparément. Le résultat final étant une table avec une liste de bases de données uniques et une table avec une liste d'informations de travail historique et une table entre celles-ci fournit un travail: à de nombreuses relations de base de données. Merci encore.

+0

nous ne sommes jamais doux, mais nous essayons d'être polis. Un bouton 101010 au-dessus de votre zone d'édition vous aidera à formater votre code dans la question. – bobs

Répondre

0

Il semble que votre solution de script soit la solution. Et vous n'avez pas besoin de vous soucier de l'insertion depuis le script.

Vous pouvez limiter le rôle du script pour gérer uniquement le fractionnement de la liste séparée par des virgules. La sortie du script serait une ligne pour chaque élément de la liste.

L'étape importante consiste à définir le composant de script pour qu'il soit asynchrone. Mode asynchrone permettant au composant de générer un nombre de lignes différent du nombre de lignes entré. Après avoir créé un nouveau composant de script en tant que transformation, modifiez le composant. Sélectionnez les propriétés d'entrée et de sortie, sélectionnez Sortie 0. Remplacez la valeur de la propriété SynchronousInputID par None. Développez la branche Output 0 et Add Columns à la branche Output Columns. Les colonnes comprendront les lignes qui sortent du composant. Enfin, ajoutez votre code de script qui créera une ligne pour chaque élément de votre liste séparée par des virgules. Une fois que vous avez exporté une ligne pour chaque élément de la liste, ajoutez une destination comme n'importe quelle autre destination.