2010-12-14 11 views
1

Dies ist mein 1. Post, also sei sanft :). Ich erstelle ein SSIS-Paket, um Informationen zu Jobs zu sammeln, die im gesamten Unternehmen bei der Arbeit laufen. Das Ziel ist es, Tabellen mit Informationen über Server, Instanzen, DBs, Jobs und deren Beziehungen zueinander zu füllen. Im Idealfall werde ich Merge-Befehle verwenden, um zu aktualisieren oder einzufügen, wo es nötig ist, aber jetzt wird es nur eingefügt. Hier ist ein Überblick über mein Paket (halten Sie Ihren Geist aus der Gosse bitte: P):Füllen Pivot-Tabelle aus einem Komma Trennzeichen Feld in einem Datenfluss-Task in SSIS

(Beschränkungen für Hyperlinks für neue Benutzer bedeuten, dass Sie benötigen, um die + Zeichen mit „t“ s in meinen Links zu ersetzen

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

Die erste Datenflußtask zieht in einer Liste von Instanzen von einer flachen Textdatei und speichert sie in einem Datensatz zu überprüfen. Die foreach-Schleife geht dann durch jede von ihnen ein und ändert meine Quelle Verbindungszeichenfolge der Connection Manager In der Schleife durchlaufen wir jeweils 1 Instanz und der Task "Process Server" Dataflow wird verwendet, um den Servernamen zu finden und ihn der Ziel-DB hinzuzufügen, falls dies nicht der Fall ist existieren, so speichert es auch die Server-ID und den Namen in Paketvariablen. Die "Prozessinstanz" tut dasselbe wie oben, aber stattdessen für die Instanz. Der Task "Collect DB Data" verwendet dann diese Paketvariablen, um alle DBs in dieser Instanz als Datensätze mit den oben für Fremdschlüssel erwähnten Paketvariablen einzufügen. Sobald das erledigt ist, gehen wir zur Aufgabe "Jobdaten sammeln" (hoffentlich die letzte Aufgabe für dieses Paket). Im Folgenden ist der Inhalt der letzten Aufgabe:

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

So innerhalb dieser Aufgabe das ist, was so weit ich tue. Ich verwende eine Abfrage, um Job-Informationen mit Wartungsplandaten zu sammeln. Hier ist die Abfrage für den OLE DB-Quelle:

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

Sorry, aber aus irgendeinem Grund dieses Forum erhalten nicht meine Formatierung. Jedenfalls füge ich dann die Package-Variable Instance ID als Spalte hinzu, um das Einfügen dieser Datensätze mit diesem Fremdschlüssel zu erleichtern. Ich konvertiere einige Zeichenketten in Unicode, was weder hier noch dort ist, und dann mache ich eine bedingte Aufteilung auf Datensätze mit einem Wartungsplan und Datensätze ohne. Für Datensätze ohne kann ich sie einfach in das Ziel einfügen und sie sind fertig! Bei Aufzeichnungen mit einem Wartungsplan ist die Wahrscheinlichkeit, dass sie eine Verbindung zu einer oder mehreren Datenbanken haben, sehr hoch. Also, 1. Ich füge den Job-Eintrag in die Job-Tabelle (genau wie ich mit Datensätzen nicht innerhalb eines Wartungsplans) und dann suche ich nach dem Datensatz, den ich gerade ID eingefügt habe. Als Nächstes nehme ich das Feld aus meiner Abfrage, das eine kommagetrennte Liste von DBs enthält, die vom Wartungsplan, zu dem dieser Job gehört, betroffen sind, und trennen ihn in eine VB.Net ArrayList. Ich weise diese ArrayList dann einer Package-Variablen zu.

Dies ist der Teil, in dem ich bin. Offensichtlich ist mein nächster Schritt, eine Art Schleife zu bilden, die die Job-ID verwendet, die ich gerade durch jede Variable in der ArrayList durchgeschaut habe, um sie 1 zu einer Zeit in die DB/Job-Pivot-Tabelle einzufügen. Problem ist mir nicht bewusst, wie ich eine Schleife innerhalb einer Datenflussaufgabe machen kann, und ich kann mir keine gute Möglichkeit vorstellen, den Pivot-Tabelleneinsatz aus dieser Aufgabe zu verschieben. Ich könnte das vielleicht mit einer Skriptkomponente machen, aber ich bin nicht sicher, wie man Einfügungen innerhalb der Skriptaufgabe ausführt (sollte ich das überhaupt berücksichtigen). Ich bin sowohl mit VB.Net und C# als auch mit TSQL vertraut, so dass ich jede Art der Implementierung untersuchen kann. Vielen Dank im Voraus für Ihre Hilfe. Prost!

PS. Hier ist die Tabellenstruktur ich die Daten am Einsetzen in:

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

Und hier einige Beispiel ergibt sich aus der Abfrage, die ich früher geschrieben.Denken Sie daran, kann das Skript gegen jede Instanz ausgeführt werden, solange Sie MSDB verwendet, weil es alle System generierten Tabellen und Views verwendet:

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

Nur nicht klar über meine Ziele für dieses Paket. JOB_NAME, CATEGORY_ID, RUN_DATE, RUN_TIME, RUN_DURATION und PLAN_NAME werden in die Tabelle TBL_SQL_JOBS aufgenommen. Die Spalte PLAN_DETAILS tut nichts für Nullen (wie auch PLAN_NAME), aber bei aufgefüllten Datensätzen wird die Zeichenfolge "Databases:" entfernt und die durch Trennzeichen getrennten Datenbanknamen aufgeteilt. Dann muss es die DB-Namen von der Aufteilung nach der TBL_Databases-Tabelle (zuvor gefüllt) überprüfen und die entsprechende ID abrufen. In Kombination mit der ID des aktuellen Job-Datensatzes, den wir verarbeiten (denken Sie an den Teil "Lookup-Job-ID" der letzten Aufgabe des Pakets), fügen wir diese Datensätze getrennt zur Tabelle TBL_DATABASE_JOBS hinzu. Das Endergebnis ist eine Tabelle mit einer Liste eindeutiger DBs und einer Tabelle mit einer Liste historischer Job-Informationen und einer Tabelle dazwischen, die eine 1-Job: zu viele DB-Beziehung bereitstellt. Danke noch einmal.

+0

wir sind nie sanft, aber wir versuchen, höflich zu sein. Über Ihrem Bearbeitungsbereich befindet sich ein 101010-Button, mit dem Sie den Code in der Frage formatieren können. – bobs

Antwort

0

Es klingt wie Ihre Skriptlösung ist der Weg zu gehen. Außerdem müssen Sie sich keine Gedanken über das Einfügen aus dem Skript machen.

Sie können die Rolle des Skripts so beschränken, dass nur die Aufteilung der durch Kommas getrennten Liste behandelt wird. Die Ausgabe des Skripts würde für jedes Element in der Liste eine Zeile sein.

Der wichtige Schritt besteht darin, die Skriptkomponente asynchron zu setzen. Asynchroner Modus mit der Möglichkeit, dass die Komponente eine andere Anzahl von Zeilen als die eingegebene Anzahl von Zeilen ausgibt. Nachdem Sie eine neue Skriptkomponente als Transformation erstellt haben, bearbeiten Sie die Komponente. Wählen Sie die Eingabe- und Ausgabeeigenschaften aus, und wählen Sie Ausgabe 0. Ändern Sie den Wert für die SynchronousInputID-Eigenschaft in None. Erweitern Sie den Zweig "Ausgabe 0" und fügen Sie dem Zweig "Ausgabespalten" Spalten hinzu. Die Spalten umfassen die Zeilen, die die Komponente verlassen. Fügen Sie schließlich Ihren Skriptcode hinzu, der eine Zeile für jedes Element in Ihrer durch Kommas getrennten Liste erstellt.

Sobald Sie für jedes Element in der Liste eine Zeile exportieren können, fügen Sie ein Ziel wie jedes andere Ziel hinzu.

Verwandte Themen