SSIS vs. SQL: Quellcodeverwaltung — warum SP-Diffs lesbar sind und `.dtsx`-Diffs nicht

Wer zwei Versionen eines SSIS-Pakets difft, sieht schon bei einer trivialen Umbenennung Änderungs-Markierungen quer durch das XML — im Beispiel dieses Artikels acht „geänderte Bereiche“, und der Diff ordnet die Umbenennung nicht einmal der richtigen Task zu. Dieselbe Modifikation in einer Stored Procedure zeigt drei Zeilen Diff und ist in 30 Sekunden reviewbar. Quellcodeverwaltung ist eine Wartbarkeits-Entscheidung: keine Tool-Frage, sondern eine Artefakt-Format-Frage.

Was dich erwartet:

  • Vergleich zweier Versionen eines T-SQL-Skripts in Visual Studio: ein klar lesbarer Inline-Diff.
  • Vergleich zweier Versionen eines SSIS-Pakets (.dtsx): selbst eine Skript-Task-Umbenennung erzeugt acht „geänderte Bereiche“ im XML, mit falscher Zuordnung der Umbenennung.
  • Komplexes Beispiel: dieselbe Hierarchie-Ranking-Aufgabe auf [DimEmployee] aus AdventureWorks — als Stored Procedure versioniert klar diffbar, als rohes .dtsx-XML faktisch nicht.
  • Versionskontrolle 2026: was sich seit dem 2018er-TFS-Stand bewegt hat — Git, Liquibase/Flyway, sqlmesh.

Voraussetzung: SQL Server 2017+ (für die Beispiele), SSIS 2017+ (Visual Studio mit SSDT), Git oder Azure DevOps Server als VCS. Die Beispiele wurden 2018 mit Visual Studio 2017 und TFS entwickelt — das Diff-Argument überträgt sich 1:1 auf den Git-zentrischen Stack von 2026.

Inhalt

Überblick

SQL Server Integration Services (SSIS) ist ein mächtiger Werkzeugkasten für die Entwicklung von ETL-Strecken. Für den Einsatz von SSIS sprechen viele gute Gründe. Es gibt aber auch genügend Gründe, die dagegen sprechen. Innerhalb des Microsoft-Produkt-Stacks kommt als Alternative für die Entwicklung komplexer ETL-Strecken im Wesentlichen nur Transact-SQL (T-SQL) in Frage.

Dieser Artikel gehört zu einer Serie von Artikeln, die wichtige Entscheidungskriterien für die Wahl der richtigen Technologie(n) — SSIS und/oder T-SQL — beleuchten.


Eine Quellcodeverwaltung ermöglicht die Speicherung von Quellcode in verschiedenen Versionen. Nach einer Änderung kann die geänderte Datei als neue Version gespeichert werden. Zu jeder Version legt die Quellcodeverwaltung außerdem Metadaten ab: Datum und Uhrzeit der Änderung, die Benutzerkennung der Person, die die Änderung durchgeführt hat, oder einen Verweis auf einen Change-Request. Ältere Versionen eines Dokumentes können damit wiederhergestellt werden.

Darüber hinaus bieten Quellcodeverwaltungen zusätzliche Features an, die unter anderem für die Arbeit in einem Mehrentwicklerteam und auch für das Releasemanagement unerlässlich sind. Kurz: Die Verwaltung von Quellcode ist ein essenzieller Bestandteil professioneller Softwareentwicklung. Im Microsoft-Umfeld ist heute Git der Standard — entweder als Repo bei GitHub/GitLab/Bitbucket oder als Selbst-Hosting via Azure DevOps Server (der Nachfolger des Team Foundation Servers, TFS). Beide lassen sich in die Entwicklungsumgebung Visual Studio integrieren.

Ein weiteres wichtiges Feature einer Quellcodeverwaltung ist der Vergleich zweier Versionen einer Datei. Über einen Vergleich zweier Versionen der gleichen Datei können die Unterschiede identifiziert werden. Ein Anwendungsfall hierfür ist zum Beispiel das Vier-Augen-Prinzip. Ein Entwickler arbeitet an einem Dokument und ein weiterer führt einen Review des geänderten Dokumentes durch. Relevant für den Review sind nur die Änderungen in der neuen Version, und genau diese liefert der Versionsvergleich.

Der Vergleich von zwei Versionen einer Datei ist allerdings nur dann hilfreich, wenn aus den ermittelten Änderungen die Bedeutung und ggf. der Grund der Änderung ersichtlich sind. Ein Vergleich von Binär-Dateien ist in der Regel nicht hilfreich, da weder die Bedeutung der Änderung noch der Grund erkennbar sind. Bei einem Vergleich von zwei Versionen einer Text-Datei ist die Wahrscheinlichkeit hoch, dass beides erkennbar ist. Etwas schwieriger wird es allerdings auch hier, wenn die Daten in der Text-Datei hierarchisch und strukturiert gespeichert sind. Aussagekräftig bleibt ein Text-Diff nur, solange die Serialisierung zwischen zwei Versionen stabil ist, solange also fachliche Änderungen nicht von Reihenfolge-, Format- und Metadaten-Änderungen überlagert werden. So werden SSIS-Pakete in einer hierarchisch strukturierten Weise in XML-Dateien mit der Extension *.dtsx gespeichert, während SQL-Statements üblicherweise als reine Text-Dateien mit der Extension *.sql gespeichert werden.

Dieser Artikel beschreibt den Versionsvergleich von SQL-Skripten und SSIS-Paketen anhand von drei Beispielen.

  • SQL-Skript im Diff: zwei Versionen im Vergleich
  • SSIS-Paket im Diff: eine Umbenennung im .dtsx-XML
  • Komplexe Entwicklungsartefakte im Diff: SQL-Statement vs. SSIS-Paket

SQL-Skript im Diff: zwei Versionen im Vergleich

Die folgende Abbildung zeigt das Ergebnis eines Vergleichs von zwei Versionen eines SQL-Skriptes in Visual Studio. In diesem Vergleich werden die Abweichungen farblich hervorgehoben.

Visual-Studio-Diff-Ansicht eines SQL-Skripts: links die Vorgänger-Version mit roten Markierungen, rechts die geänderte Version mit hellgrün hinterlegten Zeilen und kräftigem Grün für die konkreten Änderungen. Rechts neben dem vertikalen Scroll-Balken zeigt eine Spur die Position der Änderungen im Dokument.

Der Screenshot zeigt auf der linken Seite das SQL-Statement vor der Änderung und auf der rechten Seite das geänderte SQL-Statement. In dem geänderten SQL-Statement sind Zeilen, die eine Änderung enthalten, hellgrün hinterlegt. Änderungen selbst werden in einem kräftigeren Grün hervorgehoben. In der Vorgängerversion auf der linken Seite sind die korrespondierenden Texte in roter Farbe hinterlegt.

Rechts neben dem vertikalen Scroll-Balken sind die geänderten Bereiche in dem gesamten Dokument angedeutet. Eine gute Beschreibung zur Verwendung des Datei-Vergleichs findet sich in der Online-Dokumentation zu Git in Azure DevOps von Microsoft.

SSIS-Paket im Diff: eine Umbenennung im .dtsx-XML

Die Entwicklung von ETL-Strecken mit SSIS erfolgt in SSIS-Paketen. SSIS-Pakete werden als XML-Dokument gespeichert. Microsoft selbst hat zum SSIS-Paketformat unter anderem ausgeführt:

In der aktuellen Version von Integration Services wurden wichtige Änderungen am Paketformat (DTSX-Datei) vorgenommen, um das Format besser lesbar zu machen und Pakete besser vergleichen zu können. Außerdem wurde das Zusammenführen von Paketen verbessert, die keine miteinander in Konflikt stehenden Änderungen oder im Binärformat gespeicherte Änderungen enthalten.

Dieses Zitat stammt aus der historischen Microsoft-Doku zum SSIS-Paketformat — einer Seite zur Format-Überarbeitung in SQL Server 2014, die inzwischen aus der Live-Doku entfernt wurde. Die technische DTSX-Spezifikation lebt als Open Spec MS-DTSX weiter. Microsoft hat das Format also ausdrücklich mit dem Ziel weiterentwickelt, Vergleich und Zusammenführung zu verbessern. Dass ein zeilenbasierter XML-Diff damit automatisch aussagekräftig wird, folgt daraus nicht. Das zeigt das folgende Beispiel.

Die folgenden beiden Screenshots zeigen zwei Versionen eines Control Flows eines SSIS-Paketes, in dem lediglich der Name der zweiten Skript-Task von B SCT Skripttask nach D SCT Skripttask geändert wurde. Die Screenshots stammen von einem SSIS-Paket, in dem Tasks lediglich im Control Flow platziert, beliebig benannt und miteinander verbunden, aber nicht weiter konfiguriert wurden. Das Beispiel ist bewusst einfach gehalten und dient nur der Veranschaulichung.

Version 1

SSIS-Control-Flow Version 1: vier Skript-Tasks A/B/C/E vertikal platziert, durch Precedence-Constraints verbunden, keine weitere Konfiguration.

Version 2

SSIS-Control-Flow Version 2: identische Anordnung wie Version 1, aber die zweite Skript-Task ist von B SCT Skripttask auf D SCT Skripttask umbenannt.

Versionsvergleich

Der Vergleich beider Versionen fällt ernüchternd aus: Die Umbenennung einer einzigen Skript-Task resultiert in diesem Paket in acht geänderten Bereichen des XML-Dokumentes, die rechts neben dem vertikalen Scroll-Balken angezeigt werden.

Visual-Studio-Diff-Ansicht des `.dtsx`-XML zweier SSIS-Pakete: dichte rote und grüne Markierungen über das gesamte Dokument verstreut, am rechten Scroll-Balken acht klar erkennbare Cluster-Markierungen. Der inhaltliche Edit (eine Umbenennung) ist im Lärm der GUID-Reorderings nicht direkt erkennbar.

Die Zeilen 60 bis 77 repräsentieren unter anderem die Skript-Task B SCT Skripttask in der Vorgänger-Version. Diese Skript-Task wurde gemäß Vergleich nach C SCT Skripttask umbenannt und nicht nach D SCT Skripttask. Die Zeilen 78 bis 95 repräsentieren unter anderem die Skript-Task C SCT Skripttask in der Vorgänger-Version. Diese Skript-Task wurde laut Vergleich nach D SCT Skripttask umbenannt.

Noch mal zur Erinnerung: Geändert wurde lediglich der Name der Skript-Task B SCT Skripttask nach D SCT Skripttask.

Der Text-Diff arbeitet dabei technisch korrekt und beantwortet die Review-Frage trotzdem falsch: Er vergleicht Zeilen, nicht Objekte. Weil er die Identität der Tasks nicht kennt, ordnet er die eine Umbenennung zwei verschiedenen Tasks zu.

Komplexe Entwicklungsartefakte im Diff: SQL-Statement vs. SSIS-Paket

In diesem Abschnitt wird ein etwas komplexeres Beispiel betrachtet, das in einer ähnlichen Form durchaus in der Praxis zu finden sein könnte.

Aufgabe

In diesem Beispiel ist das Ranking der Mitarbeiter in der Tabelle [AdventureWorksDW2017].[DimEmployee] je Hierarchie-Stufe entlang der Urlaubs- und Krankheitszeiten der Mitarbeiter zu ermitteln. Zu jedem Mitarbeiter sind vier Kennzahlen zu berechnen. Für die Berechnung sind die bei den Kennzahlen angegebenen Window Functions von SQL Server zu verwenden:

  • Ranking Urlaubszeit, Gruppen-Einteilung: NTILE(3) — NTILE(3) verteilt die zu beurteilende Menge der Mitarbeiter (je Hierarchie-Stufe) möglichst gleichmäßig auf drei Gruppen. Ist die Anzahl der Zeilen nicht durch drei teilbar, unterscheiden sich die Gruppen um höchstens eine Zeile, die größeren Gruppen kommen zuerst. Die Zuordnung des Mitarbeiters erfolgt entsprechend der aufsteigenden Sortierung nach Urlaubszeiten.
  • Ranking Urlaubszeit, Rangfolge: DENSE_RANK — DENSE_RANK ermittelt zu jedem Mitarbeiter (je Hierarchie-Stufe) eine Position in einer Rangfolge. Liegen zu zwei Mitarbeitern z. B. die gleiche Anzahl Urlaubsstunden vor, erhalten beide Mitarbeiter die gleiche Position im Ranking.
  • Ranking Krankheitszeit, Gruppen-Einteilung: NTILE(3) — analog zu Urlaubszeit.
  • Ranking Krankheitszeit, Rangfolge: DENSE_RANK — analog zu Urlaubszeit.

Diese Aufgabe ist sowohl über ein SQL-Statement als auch über ein SSIS-Paket zu lösen. In einem zweiten Teil sind die so entwickelten Dokumente dahingehend zu ändern, dass als Ordnungskriterium nicht mehr die Urlaubs- und Krankheitszeiten, sondern das Einstellungsdatum des Mitarbeiters sowie sein Geburtsdatum zu verwenden sind.

Bei dieser Aufgabe gibt es zwei Herausforderungen:

  • Ermittlung der Hierarchie der Mitarbeiter
  • Ermittlung des Rankings

SQL-Statement

Transact-SQL stellt für beide Herausforderungen leicht verwendbare Konstrukte bereit:

Sind Daten — wie in der Tabelle [DimEmployee] — über eine Vater-Kind-Beziehung strukturiert, können diese Daten über eine rekursive CTE vergleichsweise kompakt ausgewertet werden. Das setzt eine konsistente, zyklusfreie Hierarchie mit definierter Wurzel voraus:

  1: CREATE OR ALTER PROCEDURE [dbo].[sp_insert_employee_hierarchy_ranking]
  2: AS
  3: BEGIN
  4:    SET NOCOUNT ON;
  5: 
  6:    TRUNCATE TABLE [dbo].[fct_employee_hierarchy_ranking];
  7: 
  8:    WITH CTE_Employee AS
  9:    (
 10:       -- Anker der rekursiven CTE: der CEO als Top-Level-Mitarbeiter ohne
 11:       -- Vorgesetzten. [Level] = 1 markiert die Wurzel der Hierarchie.
 12:       SELECT
 13:           [EmployeeKey]
 14:          ,[FirstName]
 15:          ,[LastName]
 16:          ,[Title]
 17:          ,[ParentEmployeeKey]
 18:          ,[VacationHours]
 19:          ,[SickLeaveHours]
 20:          ,1 AS [Level]
 21:       FROM
 22:           [AdventureWorksDW2017].[dbo].[DimEmployee]
 23:       WHERE
 24:           [ParentEmployeeKey] IS NULL
 25:       AND [Status] = N'Current'
 26: 
 27:       UNION ALL
 28: 
 29:       -- Rekursionsschritt: alle Mitarbeiter, deren [ParentEmployeeKey] auf
 30:       -- einen bereits in der CTE enthaltenen Mitarbeiter zeigt. [Level] wird
 31:       -- pro Tiefe um 1 erhöht. T-SQL verbindet Anker und rekursives Glied
 32:       -- mit UNION ALL.
 33:       SELECT
 34:           T01.[EmployeeKey]
 35:          ,T01.[FirstName]
 36:          ,T01.[LastName]
 37:          ,T01.[Title]
 38:          ,T01.[ParentEmployeeKey]
 39:          ,T01.[VacationHours]
 40:          ,T01.[SickLeaveHours]
 41:          ,T00.[Level] + 1 AS [Level]
 42:       FROM
 43:          [AdventureWorksDW2017].[dbo].[DimEmployee] AS T01
 44:       INNER JOIN
 45:          CTE_Employee AS T00
 46:          ON
 47:            T01.[ParentEmployeeKey] = T00.[EmployeeKey]
 48:       WHERE
 49:          T01.[Status] = N'Current'
 50:    )
 51:    INSERT INTO [dbo].[fct_employee_hierarchy_ranking]
 52:    (
 53:        [ParentEmployeeKey]
 54:       ,[EmployeeKey]
 55:       ,[LastName]
 56:       ,[FirstName]
 57:       ,[Title]
 58:       ,[Level]
 59:       ,[VacationHours]
 60:       ,[SickLeaveHours]
 61:       ,[VacationHours_NTILE]
 62:       ,[VacationHours_DENSE_RANK]
 63:       ,[SickLeaveHours_NTILE]
 64:       ,[SickLeaveHours_DENSE_RANK]
 65:    )
 66:    SELECT
 67:        [ParentEmployeeKey]
 68:       ,[EmployeeKey]
 69:       ,[LastName]
 70:       ,[FirstName]
 71:       ,[Title]
 72:       ,[Level]
 73:       ,[VacationHours]
 74:       ,[SickLeaveHours]
 75:       ,NTILE(3)     OVER (PARTITION BY [Level] ORDER BY [VacationHours],  [EmployeeKey]) AS [VacationHours_NTILE]
 76:       ,DENSE_RANK() OVER (PARTITION BY [Level] ORDER BY [VacationHours])                 AS [VacationHours_DENSE_RANK]
 77:       ,NTILE(3)     OVER (PARTITION BY [Level] ORDER BY [SickLeaveHours], [EmployeeKey]) AS [SickLeaveHours_NTILE]
 78:       ,DENSE_RANK() OVER (PARTITION BY [Level] ORDER BY [SickLeaveHours])                AS [SickLeaveHours_DENSE_RANK]
 79:    FROM
 80:       CTE_Employee;
 81: END;
 82: GO

Dieses Statement liefert zu jedem Mitarbeiter in der Spalte [Level] die Hierarchiestufe sowie die geforderten Kennzahlen entlang der Urlaubs- und Krankheitszeiten. Ein Detail der OVER-Klauseln ist bewusst asymmetrisch: Bei NTILE(3) sorgt [EmployeeKey] als eindeutiger zweiter Sortierschlüssel dafür, dass Gleichstände in den Stunden-Werten deterministisch auf die drei Gruppen verteilt werden — gleiche Stunden-Werte können dabei trotzdem in verschiedenen Gruppen landen. Bei DENSE_RANK bleibt er weg, damit Gleichstände tatsächlich die gleiche Position erhalten. Mit dem AdventureWorksDW2017-Datensatz und dem Filter auf [Status] = N'Current' liefert die Abfrage 254 Mitarbeiter auf fünf Hierarchiestufen. Für diese fünf Ebenen reicht die Standard-Rekursionsgrenze von SQL Server (100 Rekursionen) aus. Bei tieferen Hierarchien ist OPTION (MAXRECURSION n) zu beachten.

Ergebnis-Tabelle aus dem T-SQL-Statement: 254 Zeilen mit den Spalten ParentEmployeeKey, EmployeeKey, LastName, FirstName, Title, Level (1 bis 5), VacationHours, SickLeaveHours sowie den vier Ranking-Spalten je NTILE und DENSE_RANK.

Der Vergleich des oben gezeigten Statements mit einer modifizierten Version, in der VacationHours durch HireDate und SickLeaveHours durch BirthDate ersetzt sind, ist das erste Diff-Beispiel dieses Artikels: drei Spalten-Edits und ein Datentyp-Wechsel. Der Diff in Visual Studio zeigt die rund 15 betroffenen Zeilen klar lokalisiert.

SSIS-Paket

So einfach diese Aufgabe über T-SQL zu lösen war, so schwer und vor allem zeitraubend war die Entwicklung der Lösung in SSIS, wenn sie ausschließlich mit SSIS-Komponenten (einschließlich Skript-Komponenten) und ohne ausgelagertes SQL entstehen soll. Für die hier dargestellte Lösung wurde der folgende Ansatz gewählt:

  • Je Aufgabe werden zwei Tabellen in der Datenbank benötigt.
  • Der Data Flow 1000 DFT Calculate Levels ermittelt ausschließlich die Hierarchie.
  • Der Data Flow 3000 DFT Calculate Ranking ermittelt ausschließlich das Ranking und speichert das Ergebnis in einer Tabelle.
  • Das Ranking wird für Urlaubs-/Krankheitszeiten jeweils über zwei Skript-Tasks ermittelt.

Möglicherweise gibt es auch eine viel einfachere Lösung.

Control Flow

Der Control Flow des SSIS-Paketes gestaltet sich mit vier Tasks noch recht einfach.

SSIS-Control-Flow des komplexen Beispiels: vier Tasks sequenziell verbunden — Truncate, Calculate Levels, Level Counts, Calculate Ranking.

Data Flow 1000 DFT Calculate Levels

Während T-SQL die Hierarchie rekursiv auswerten kann, bietet der SSIS-Datenfluss selbst keinen direkt vergleichbaren Mechanismus an. Soll die Rekursion nicht in eine SQL-Abfrage ausgelagert werden, muss die Zuordnung der fünf Hierarchiestufen je Stufe entwickelt werden — die Lösung ist damit an die konkrete Tiefe der Hierarchie gebunden.

Data Flow `1000 DFT Calculate Levels`: hochkant ausgerichtetes Layout mit über 30 Tasks (Multicast, Sort, Merge Join, Derived Column für die Levels 2 bis 5) — die nicht-generische Hierarchie-Berechnung füllt einen vollen Bildschirm.

Data Flow 3000 DFT Calculate Ranking

Die eigentliche Berechnung des Rankings erfolgt in Skript-Tasks durch den Vergleich zweier aufeinanderfolgender Datensätze.

Data Flow `3000 DFT Calculate Ranking`: zwei Sort-Tasks (eine nach Level + VacationHours, eine nach Level + SickLeaveHours), zwei Script-Tasks zur Berechnung der NTILE/DENSE_RANK-Spalten, abschließendes OLE-DB-Destination in die Zieltabelle.

Anpassung des Paketes

Nach der Fertigstellung wurde das Paket in Git committet (bzw. in einem On-Prem-Stack via Azure DevOps Server) und anschließend so abgeändert, dass als Ordnungskriterium für die Ermittlung des Rankings nicht mehr die Urlaubs- und Krankheitszeiten, sondern das Einstellungs- und Geburtsdatum verwendet werden. Im Wesentlichen waren drei Feldnamen und ein Datentyp zu ändern. Trotz der größeren Anzahl verwendeter Tasks hielt sich der Umfang der Änderungen in Grenzen. Die Anpassung war in diesem Beispiel in wenigen Minuten erledigt, und das Ergebnis wurde ebenfalls committet.

Vergleich der zwei Versionen

Ein Vergleich der beiden committeten Versionen ergab die folgenden Änderungen:

Visual-Studio-Diff-Ansicht des `.dtsx`-XML der zwei SSIS-Paket-Versionen: das ganze Dokument ist von roten und grünen Markierungen durchzogen, der rechte Scroll-Balken zeigt dutzende Fundstellen, obwohl fachlich nur drei Feldnamen und ein Datentyp geändert wurden.

Maßgeblich für die Beurteilung des Umfangs der Änderung im SSIS-Paket ist der rechte vertikale Scroll-Balken. Rechts neben dem Scroll-Balken sind die Fundstellen der Änderungen im SSIS-Paket in grüner und roter Farbe hervorgehoben. Der Diff markiert Änderungen über weite Teile der XML-Datei, obwohl fachlich nur drei Feldnamen und ein Datentyp geändert wurden.

Während die Lösung der Aufgabe in T-SQL relativ schnell entwickelt werden kann, ist die Entwicklung des SSIS-Paketes aufwändig und hat mehrere Stunden gekostet. Grund hierfür waren im Wesentlichen die schlechte Lesbarkeit eines SSIS-Paketes, aber auch die überraschende Erkenntnis, dass es für die vermeintlich einfache Aufgabe in SSIS keinen fertigen Lösungsansatz gibt. Der erste Versuch, die Hierarchie-Ebene über eine einzige Formel-Expression zu berechnen, endete nach zwei bis drei Stunden mit der weißen Fahne. Übrig blieb der hier dargestellte Ansatz — auf Kosten der Lesbarkeit und Flexibilität des SSIS-Paketes.

Nach Fertigstellung und einem Commit der Lösung waren nur noch vergleichsweise wenige Änderungen erforderlich, um das Ordnungskriterium für die Berechnung der Kennzahlen zu ändern. Die wenigen Änderungen resultieren in zahlreichen Änderungen im zugrunde liegenden XML-Dokument. Ein Versionsvergleich lässt den Entwickler ratlos zurück, was sich denn nun im SSIS-Paket geändert hat. Zum Vergleich mit T-SQL: Dort ist der Lösungsweg direkt lesbar, und Änderungen sind nachvollziehbar.

Das Beispiel belegt damit zwei voneinander unabhängige Befunde: Die Implementierung ist in T-SQL kompakter, und das SSIS-Artefakt erzeugt trotz kleiner fachlicher Änderung einen für Reviews unbrauchbaren Versionsvergleich. Für die Diff-Frage dieses Artikels trägt der zweite Befund die Last.

Versionskontrolle 2026

Die Kernfrage des Artikels — wie diffbar ist ein Entwicklungs-Artefakt — ist 2026 keine reine SQL-Server-/SSIS-Frage mehr. Der Microsoft-Stack ist nur eine Option neben anderen, und das Diff-Pragmatik-Argument überträgt sich Stack-übergreifend.

Microsoft-Stack heute

Visual-Studio-Datenbankprojekte gibt es heute in zwei Varianten: das klassische SSDT mit .sqlproj-Projekten auf MSBuild-Basis und das SDK-style-Format Microsoft.Build.Sql, das Standard-Format der SQL Database Projects Extension für VS Code. Die Grenze verläuft quer durch die Visual-Studio-Welt (Stand: September 2026): Visual Studio 2022 enthält SDK-style als Preview-Komponente, Visual Studio 2026 unterstützt ausschließlich das klassische Format. SSIS-Projekte laufen separat über die SSIS-Projects-Extension, die Visual Studio 2022 und 2026 unterstützt. Die Pakete sind weiter .dtsx-basierte XML-Artefakte, in denen Reihenfolge-, Layout- und Metadaten-Änderungen einen textuellen Diff weiterhin mit erheblichem Rauschen überlagern. Microsoft selbst hat den Versionskontroll-Standard von Team Foundation Server (TFS) auf Git verschoben: Azure DevOps Server ist der TFS-Nachfolger und unterstützt Git als modernes VCS-Backend. TFVC bleibt für Bestands-Kompatibilität optional verfügbar. Der Cloud-Wechsel zu Azure Data Factory und Synapse Pipelines stellt dieselbe Frage in JSON statt XML: Auch dort entscheiden Struktur und Änderungsmuster der Pipeline-Definitionen über die Diff-Qualität — das Tool wechselt, die Artefakt-Format-Frage bleibt.

Git-Welt

Außerhalb des Microsoft-Stacks ist die Welt offener und SQL-zentrierter. Drei Bausteine prägen die Versionskontroll-Praxis 2026:

  • Git als Default-VCS: GitHub, GitLab und Bitbucket sind die üblichen Heimaten. Stored-Procedure-Diffs (SP-Diffs) sind Plain-Text-Diffs und lassen sich in jedem Pull-Request-Tool ohne Zusatzwerkzeug reviewen.
  • Schema-Migrations-Tools: Liquibase und Flyway versionieren Schema-Änderungen typischerweise als Plain-SQL-Migrations-Files (Liquibase-Changelogs alternativ als XML, YAML oder JSON). Jede V001__add_employee_table.sql ist diffbar wie ein normales SQL-Skript.
  • sqlmesh: sqlmesh versioniert SQL-Pipelines mit explizitem State-Tracking. Die .sql-Files sind die versionierten Artefakte, der Wartbarkeits-Hebel beim Diffen ist derselbe wie bei einer Stored Procedure, nur stack-agnostisch (Postgres, Snowflake, BigQuery, Redshift, Databricks, ClickHouse, DuckDB, Microsoft Fabric u. a.). Seit März 2026 ist sqlmesh ein Linux-Foundation-Projekt (eingebracht von Fivetran) und steht damit unter Community-Governance statt Single-Vendor.

Die Frage bleibt: was lässt sich diffen?

Stack-übergreifend bleibt dieselbe Pointe bestehen: Das Artefakt-Format entscheidet, nicht das Tool: Es bestimmt, ob ein Standard-Diff genügt oder ein strukturverstehendes Werkzeug nötig ist. Plain-SQL lässt sich trivial diffen — egal ob Stored Procedure, Liquibase-/Flyway-Migration oder sqlmesh-Model. XML- und JSON-Artefakte mit GUID- und Position-Properties wie .dtsx oder ADF-Pipeline-JSON lassen sich zwar ebenfalls textuell diffen, erzeugen bei strukturellen Änderungen aber einen für Reviews kaum brauchbaren Diff. Dort helfen strukturorientierte Vergleichswerkzeuge.

Wer ETL-Logik versionierbar halten will, packt die Substanz in SQL-Artefakte. Wer SSIS-Pakete trotzdem versionieren muss, ergänzt das mit:

  • BI Developer Extensions (ehemals BIDS Helper) oder das SSIS Compare and Merge Tool aus dem Visual Studio Marketplace liefern einen Strukturbaum-Diff statt des XML-Diffs. Beides sind Community-Tools — ein offizielles SSDT-Feature mit GUID-Filter gibt es nicht.
  • Ein Screenshot-Diff der Control-Flow- und Data-Flow-Designer ist der visuelle Pragmatik-Ansatz.
  • Begleitende SQL-Skripte in Stored Procedures auslagern, sodass das .dtsx nur Orchestrierung enthält und der inhaltliche Diff im SQL-Skript stattfindet. Das ist die strategische Antwort — und SSIS vs. SQL: Lesbarkeit und Wartbarkeit macht denselben Punkt aus der Wartbarkeits-Perspektive.

Take-Away

  • SP-Diffs sind mit Standard-Diff-Tools lesbar, rohe .dtsx-Diffs sind es nicht. Das ist eine strategische Architektur-Entscheidung, keine Tool-Frage — und sie überträgt sich Stack-übergreifend auf jedes XML-/JSON-basierte ETL-Format.
  • Trivialer Edit ≠ trivialer Diff. Eine Skript-Task-Umbenennung erzeugt im Beispiel dieses Artikels acht XML-Bereich-Markierungen, und der Diff ordnet die Umbenennung der falschen Task zu. Wer Audit-Trail will, muss das Artefakt-Format diff-tauglich wählen.
  • Versionierbarkeit ist Artefakt-Format-Frage. Plain-SQL gewinnt: Stored Procedure, Liquibase-/Flyway-Migration, sqlmesh-Model. XML-/JSON-Artefakte lassen sich zwar textuell diffen, lesbar werden die Diffs aber erst mit strukturorientierten Werkzeugen.
  • Strategische Antwort: ETL-Substanz in SQL-Artefakte auslagern, .dtsx-Pakete (oder ADF-Pipelines) als reine Orchestrierung halten. Die fachliche Änderung bleibt damit im SQL-Diff nachvollziehbar — den vollständigen Audit-Trail liefern Diff, Commit-Metadaten und Review zusammen.

FAQ

Kann ich .dtsx-Pakete überhaupt sinnvoll in Git versionieren?

Ja, aber der Diff-Wert ist begrenzt. .dtsx-Dateien lassen sich problemlos committen und versionieren. Aussagekräftige Diffs und konfliktarme Merges sind dagegen deutlich schwieriger, weil beim Speichern neben der fachlichen Änderung auch Reihenfolge-, Layout- und Metadaten-Änderungen im XML landen können. Die praktische Antwort lautet: committen und zusätzlich einen lesbaren Begleit-Diff pflegen — über die BI Developer Extensions, einen Screenshot-Diff oder die SQL-Auslagerung.

Warum zeigt Git bei .dtsx-Dateien so viele Änderungen an?

SSIS speichert beim Editieren nicht nur die fachliche Änderung. Je nach Paket und Änderung wandern zusätzlich Reihenfolge-, Layout- und Metadaten-Änderungen (etwa Versions-Properties) ins .dtsx-XML. Ein zeilenbasierter Diff markiert deshalb Änderungen quer durch die Datei, selbst wenn fachlich nur ein Name oder eine Spalte gewechselt hat. Abhilfe schaffen strukturorientierte Diff-Tools oder die Auslagerung der Logik in SQL-Artefakte.

Welches Tool zeigt mir einen lesbaren .dtsx-Diff?

Es gibt mehrere Annäherungen: (1) BI Developer Extensions (ehemals BIDS Helper) für Visual Studio mit Smart-Diff (Strukturbaum-Diff statt XML-Diff) — Marketplace-Builds offiziell für VS 2017/2019, kein vollständiger VS-2022-Build verfügbar. (2) SSIS Compare and Merge Tool aus dem Visual Studio Marketplace, ein Community-Tool, das den GUID-Rausch-Filter abdeckt und auch in VS 2022 läuft. SSDT selbst liefert keinen .dtsx-spezifischen Diff. (3) Ein Screenshot-Diff der Control-Flow- und Data-Flow-Designer ist pragmatisch: kein versionierter Diff, aber visuell direkt erfassbar. Jeder dieser drei Wege ist besser als der naive XML-Diff.

Wie organisiere ich ein gemischtes Repository mit Stored Procedures und SSIS-Paketen?

Bewährt hat sich die Trennung nach Artefakt-Typ: SQL-Artefakte (Stored Procedures, Views, Functions) liegen als Visual-Studio-Datenbankprojekt mit *.sql-Files vor, SSIS-Pakete als separates Visual-Studio-Integration-Services-Projekt mit *.dtsx-Files. Beide Projekte landen im selben Git-Repo, aber die Pull-Request-Reviews unterscheiden sich je Artefakt-Typ: SQL-Diffs sind als Plain-Text reviewbar, SSIS-Diffs brauchen den Strukturbaum-Diff oder das Screenshot-Komplement. Als Best Practice gilt, möglichst viel SQL in Stored Procedures auszulagern — dann reduziert sich das .dtsx-Diff-Problem auf reine Orchestrierungs-Edits.

Wie sieht ein SP-Diff im typischen Pull-Request-Workflow aus?

Ein SP-Diff umfasst in einer GitHub-, GitLab- oder Azure-DevOps-PR-Ansicht typischerweise wenige Zeilen: geänderte sind hellgrün markiert, gelöschte rot. Der Reviewer sieht auf einen Blick, dass [VacationHours] durch [HireDate] ersetzt wurde und der Spalten-Datentyp von smallint auf date gewechselt hat. Genau dieses Pattern macht den Audit-Trail einer ETL-Pipeline überhaupt erst praktikabel.

Verwandte Artikel

SSIS-vs.-SQL-Cluster:

ETL-Kontext: