Code & Queries

Code & Queries: Your Source for SQL, Python, and AI Insights

SQL & Datenbanken

Alles rund um SQL, Datenbanken und Datenmanagement. Von einfachen Queries bis hin zu komplexen Datenbankoptimierungen – hier findest du Tutorials, Tipps und Best Practices.

Einführung

Die HIERARCHYID-Datenstruktur ist ein leistungsstarker Datentyp in SQL Server, der speziell für die Modellierung hierarchischer Daten konzipiert wurde. Statt komplexe Selbstbeziehungen oder rekursive Abfragen zu verwenden, bietet HIERARCHYID eine effiziente Möglichkeit, Baumstrukturen wie Organisationen, Dateisysteme oder Kategorien zu verwalten.

In diesem Artikel werden wir uns mit verschiedenen Szenarien beschäftigen, in denen HIERARCHYID eingesetzt werden kann, sowie ausführliche Beispiele und Skripte zur Demonstration bereitstellen.


Was ist HIERARCHYID?

HIERARCHYID ist ein systemdefinierter Datentyp in SQL Server, der es ermöglicht, hierarchische Strukturen in einer einzigen Spalte darzustellen. Jeder Knoten in der Hierarchie wird durch einen Wert dieses Datentyps identifiziert, der Informationen über seine Position im Baum enthält.

Vorteile:

  1. Effiziente Speicherung und Abfrage von Hierarchien.
  2. Einfache Navigation zwischen Eltern-, Kindern- und Geschwisterknoten.
  3. Unterstützung rekursiver Operationen ohne explizite Rekursion.
  4. Integrierte Methoden zur Manipulation und Analyse der Hierarchie.

Szenario 1: Organisationshierarchie

Eine gängige Anwendung von HIERARCHYID ist die Modellierung von Organisationen, bei denen Mitarbeiter in einer Hierarchie organisiert sind (z.B. Manager, Teamleiter, Mitarbeiter).

Beispiel:

-- Tabelle erstellen
CREATE TABLE Employees (
    EmployeeID INT PRIMARY KEY IDENTITY,
    Name NVARCHAR(100),
    OrganizationNode HIERARCHYID NOT NULL,
    OrganizationLevel AS OrganizationNode.GetLevel(), -- Berechnete Spalte für die Hierarchieebene
    Position NVARCHAR(50)
);

-- Wurzelknoten (CEO) einfügen
INSERT INTO Employees (Name, OrganizationNode, Position)
VALUES ('John Doe', '/1/', 'CEO');

-- Untergeordnete Mitarbeiter hinzufügen
DECLARE @CEO HIERARCHYID = (SELECT OrganizationNode FROM Employees WHERE Name = 'John Doe');
INSERT INTO Employees (Name, OrganizationNode, Position)
VALUES 
('Jane Smith', @CEO.GetDescendant(NULL, NULL), 'Manager'),
('Mike Brown', @CEO.GetDescendant(NULL, NULL), 'Manager');

-- Weitere Untergeordnete Mitarbeiter
DECLARE @Manager1 HIERARCHYID = (SELECT OrganizationNode FROM Employees WHERE Name = 'Jane Smith');
INSERT INTO Employees (Name, OrganizationNode, Position)
VALUES 
('Alice Johnson', @Manager1.GetDescendant(NULL, NULL), 'Team Lead'),
('Bob Wilson', @Manager1.GetDescendant(NULL, NULL), 'Developer');

Abfragen:

  1. Alle Untergeordneten eines Mitarbeiters anzeigen:

    SELECT * 
    FROM Employees 
    WHERE OrganizationNode.IsDescendantOf((SELECT OrganizationNode FROM Employees WHERE Name = 'John Doe')) = 1;
    
  2. Hierarchieebenen anzeigen:

    SELECT Name, Position, OrganizationLevel
    FROM Employees
    ORDER BY OrganizationNode;
    

Szenario 2: Produktkategorien

Ein weiteres häufiges Szenario ist die Modellierung von Produktkategorien in einem Online-Shop, wo Produkte in einer Baumstruktur organisiert sind.

Beispiel:

-- Tabelle erstellen
CREATE TABLE Categories (
    CategoryID INT PRIMARY KEY IDENTITY,
    Name NVARCHAR(100),
    CategoryNode HIERARCHYID NOT NULL,
    CategoryLevel AS CategoryNode.GetLevel()
);

-- Wurzelknoten (Wurzelkategorie) einfügen
INSERT INTO Categories (Name, CategoryNode)
VALUES ('Electronics', '/1/');

-- Unterkategorien hinzufügen
DECLARE @Electronics HIERARCHYID = (SELECT CategoryNode FROM Categories WHERE Name = 'Electronics');
INSERT INTO Categories (Name, CategoryNode)
VALUES 
('Smartphones', @Electronics.GetDescendant(NULL, NULL)),
('Laptops', @Electronics.GetDescendant(NULL, NULL));

-- Weitere Unterkategorien
DECLARE @Smartphones HIERARCHYID = (SELECT CategoryNode FROM Categories WHERE Name = 'Smartphones');
INSERT INTO Categories (Name, CategoryNode)
VALUES 
('Android Phones', @Smartphones.GetDescendant(NULL, NULL)),
('iPhone', @Smartphones.GetDescendant(NULL, NULL));

Abfragen:

  1. Alle Unterkategorien einer Kategorie anzeigen:

    SELECT * 
    FROM Categories 
    WHERE CategoryNode.IsDescendantOf((SELECT CategoryNode FROM Categories WHERE Name = 'Electronics')) = 1;
    
  2. Kategorien nach Hierarchieebenen sortieren:

    SELECT Name, CategoryLevel
    FROM Categories
    ORDER BY CategoryNode;
    

Szenario 3: Dateisystemmodellierung

HIERARCHYID kann auch verwendet werden, um Dateisysteme zu modellieren, wobei Ordner und Dateien in einer Baumstruktur organisiert sind.

Beispiel:

-- Tabelle erstellen
CREATE TABLE FileSystem (
    ItemID INT PRIMARY KEY IDENTITY,
    Name NVARCHAR(100),
    ItemType NVARCHAR(10), -- Folder oder File
    PathNode HIERARCHYID NOT NULL,
    PathLevel AS PathNode.GetLevel()
);

-- Wurzelordner erstellen
INSERT INTO FileSystem (Name, ItemType, PathNode)
VALUES ('C:', 'Folder', '/1/');

-- Unterordner hinzufügen
DECLARE @Root HIERARCHYID = (SELECT PathNode FROM FileSystem WHERE Name = 'C:');
INSERT INTO FileSystem (Name, ItemType, PathNode)
VALUES 
('Users', 'Folder', @Root.GetDescendant(NULL, NULL)),
('Program Files', 'Folder', @Root.GetDescendant(NULL, NULL));

-- Dateien im Unterordner
DECLARE @Users HIERARCHYID = (SELECT PathNode FROM FileSystem WHERE Name = 'Users');
INSERT INTO FileSystem (Name, ItemType, PathNode)
VALUES 
('user1.txt', 'File', @Users.GetDescendant(NULL, NULL)),
('user2.txt', 'File', @Users.GetDescendant(NULL, NULL));

Abfragen:

  1. Alle Elemente in einem Ordner anzeigen:

    SELECT * 
    FROM FileSystem 
    WHERE PathNode.IsDescendantOf((SELECT PathNode FROM FileSystem WHERE Name = 'C:')) = 1;
    
  2. Pfad eines Elements anzeigen:

    SELECT Name, PathNode.ToString() AS FullPath
    FROM FileSystem;
    

Zusammenfassung

Die HIERARCHYID-Datenstruktur ist eine mächtige Lösung für die Modellierung und Verwaltung von hierarchischen Daten in SQL Server. Sie vereinfacht komplexe Abfragen und Optimierungen erheblich, insbesondere in Fällen wie Organisationen, Produktkategorien oder Dateisystemen.

Schlüsselpunkte:

  • HIERARCHYID speichert die Hierarchieposition in einer kompakten Form.
  • Es bietet integrierte Methoden zur Navigation und Manipulation.
  • Die Performance ist deutlich besser als bei traditionellen Ansätzen mit rekursiven Abfragen.

Fühlen Sie sich frei, diese Beispiele anzupassen und in Ihren eigenen Projekten zu implementieren!

Die effiziente Verwaltung von Materiallisten (englisch: Bills of Materials, BOMs) ist ein zentraler Bestandteil der Produktions- und Fertigungsprozesse in der modernen Industrie. In der Welt der Datenbanktechnologie bietet Microsoft SQL Server eine leistungsstarke Lösung für die Organisation und Abfrage von hierarchischen Strukturen, insbesondere durch den Einsatz des Datentyps hierarchyid. Dieser Artikel führt Sie durch die Grundlagen der Verwendung von hierarchyid in SQL Server, um komplexe BOM-Strukturen zu erstellen und zu verwalten. Wir werden uns dabei auf das AdventureWorks-Beispiel konzentrieren, das häufig als Referenzmodell in der Praxis genutzt wird.


Was ist eine Bill of Materials (BOM)?

Eine Bill of Materials ist eine detaillierte Liste aller Komponenten und Versammlungen, die benötigt werden, um ein Endprodukt herzustellen. Diese Liste kann mehrere Ebenen tief gehen, da einzelne Komponenten selbst wieder aus weiteren Unterkomponenten bestehen können. Die Struktur einer BOM ist daher meistens hierarchisch, was bedeutet, dass sie sich wie ein Baumstruktur darstellt.

In der Praxis wird eine BOM verwendet, um: - Die erforderlichen Materialien für die Produktion zu identifizieren. - Die Kosten eines Produkts zu berechnen. - Die Verfügbarkeit von Materialien im Lager zu überprüfen. - Die Produktionsabläufe zu planen.


Warum hierarchyid für BOMs verwenden?

Der hierarchyid-Datentyp in SQL Server wurde speziell entwickelt, um hierarchische Datenstrukturen zu modellieren und abzufragen. Er ermöglicht es, komplexe Baumstrukturen wie BOMs effizient zu speichern und zu navigieren. Im Gegensatz zu rekursiven CTEs (Common Table Expressions), die ebenfalls zur Bearbeitung hierarchischer Daten verwendet werden können, bietet hierarchyid folgende Vorteile: - Leistung: Schnellere Abfragen durch direkte Vergleiche anstatt rekursiver Aufrufe. - Speicherplatz: Effizientere Speicherung der Hierarchieinformationen. - Funktionalität: Integrierte Methoden zur Bearbeitung und Analyse der Hierarchie (z. B. GetLevel, GetAncestor, IsDescendantOf).


Beispieldatenbank: AdventureWorks

Um die Funktionalität von hierarchyid zu demonstrieren, nutzen wir die AdventureWorks-Datenbank, die von Microsoft zur Verfügung gestellt wird. Diese Datenbank enthält eine Tabelle namens Production.BillOfMaterials, die die Zusammenhänge zwischen verschiedenen Produkten und deren Komponenten beschreibt.

Schemaübersicht

  • Product: Enthält Informationen über Produkte, einschließlich ihrer Kategorie und Unterkategorie.
  • ProductSubcategory: Definiert die Unterkategorien der Produkte.
  • ProductCategory: Definiert die Hauptkategorien der Produkte.
  • BillOfMaterials: Beschreibt die Zusammenhänge zwischen Versammlungen (ProductAssemblyID) und Komponenten (ComponentID).

Die wichtigsten Spalten in der BillOfMaterials-Tabelle sind: - ProductAssemblyID: Identifiziert die Versammlung (Parent). - ComponentID: Identifiziert die Komponente (Child). - BOMLevel: Gibt die Hierarchiestufe an. - PerAssemblyQty: Gibt die Menge der Komponente pro Versammlung an.


Eltern- und Kinderknoten in einer BOM

Elternknoten (Parent)

Ein Elternknoten in einer BOM ist ein Produkt oder eine Versammlung, die aus einer oder mehreren Komponenten besteht. In der AdventureWorks-Datenbank wird dies durch die Spalte ProductAssemblyID repräsentiert. Der Elternknoten steht ganz oben in der Hierarchie und wird auch als Wurzelknoten bezeichnet.

Beispiel: - Das Fahrradmodell "Mountain-100 Black, 38" mit der ProductID = 775 ist der Elternknoten.

Kinderknoten (Child)

Ein Kinderknoten in einer BOM ist eine Komponente, die Teil eines Elternknotens ist. In der AdventureWorks-Datenbank wird dies durch die Spalte ComponentID repräsentiert. Jeder Kinderknoten kann selbst wieder ein Elternknoten sein, wenn er aus weiteren Unterkomponenten besteht.

Beispiel: - Die Komponenten Rahmen, Gabel und Lenker sind Kinderknoten des Fahrrads "Mountain-100 Black, 38".


Schritt-für-Schritt-Anleitung zur Erstellung einer BOM mit hierarchyid

1. Initialisierung der Hierarchie

Um eine BOM mit hierarchyid zu erstellen, müssen wir zunächst jedem Knoten in der Hierarchie einen eindeutigen Wert zuweisen. Dieser Wert kann entweder im Dezimalformat (z. B. /1/, /2/) oder im Hexadezimalformat (z. B. 0x68) gespeichert werden.

Beispiel-Script:

-- Deklariere die Wurzelknoten-ID
DECLARE @root_pid INT = 775; -- ProductID für "Mountain-100 Black, 38"

-- Temporäre Tabelle für Ebene 1
SELECT 
    '/' AS Ancestor_NodeString,
    '/' + CAST(ROW_NUMBER() OVER (ORDER BY ComponentID) AS NVARCHAR(20)) + '/' AS Comp_NodeString,
    [ProductCategory].Name AS cat_name,
    [ProductSubcategory].Name AS sub_cat_name,
    (SELECT Name FROM Production.Product WHERE ProductID = @root_pid) AS assembly_name,
    [BillOfMaterialsID],
    [ProductAssemblyID],
    [ComponentID],
    [UnitMeasureCode],
    [BOMLevel],
    [PerAssemblyQty]
INTO #bom_level_1_comps
FROM [AdventureWorks2014].[Production].[BillOfMaterials]
LEFT JOIN AdventureWorks2014.Production.Product
    ON [BillOfMaterials].ProductAssemblyID = Product.ProductID
LEFT JOIN AdventureWorks2014.Production.ProductSubcategory
    ON Product.ProductSubcategoryID = ProductSubcategory.ProductSubcategoryID
LEFT JOIN AdventureWorks2014.Production.ProductCategory
    ON ProductCategory.ProductCategoryID = ProductSubcategory.ProductCategoryID
WHERE ProductAssemblyID = @root_pid AND EndDate IS NULL;

-- Zeige die Ergebnisse für Ebene 1
SELECT * FROM #bom_level_1_comps ORDER BY Comp_NodeString;

2. Hinzufügen weiterer Ebenen

Nachdem wir die ersten Komponenten erfasst haben, fügen wir schrittweise weitere Ebenen hinzu, indem wir die Kinderknoten der aktuellen Ebene abrufen.

Beispiel-Script für Ebene 2:

-- Temporäre Tabelle für Ebene 2 ohne NodeString
SELECT 
    ROW_NUMBER() OVER (PARTITION BY productassemblyid ORDER BY ComponentID) AS c_w_a,
    ProductAssemblyID AS productassemblyid_pid,
    *
INTO #bom_level_2_comps_wo_cns
FROM [AdventureWorks2014].[Production].[BillOfMaterials]
WHERE ProductAssemblyID IN (
    SELECT ComponentID 
    FROM [AdventureWorks2014].[Production].[BillOfMaterials] 
    WHERE EndDate IS NULL AND ProductAssemblyID = @root_pid
);

-- Generiere NodeStrings für Ebene 2
SELECT 
    #bom_level_1_comps.Comp_NodeString AS Ancestor_NodeString,
    #bom_level_1_comps.Comp_NodeString + CAST(#bom_level_2_comps_wo_cns.c_w_a AS NVARCHAR(20)) + '/' AS Comp_NodeString,
    #bom_level_1_comps.cat_name,
    #bom_level_1_comps.sub_cat_name,
    (SELECT Name FROM Production.Product WHERE ProductID = #bom_level_2_comps_wo_cns.ProductAssemblyID) AS assembly_name,
    #bom_level_2_comps_wo_cns.BillOfMaterialsID,
    #bom_level_2_comps_wo_cns.ProductAssemblyID,
    #bom_level_2_comps_wo_cns.ComponentID,
    #bom_level_2_comps_wo_cns.UnitMeasureCode,
    #bom_level_2_comps_wo_cns.BOMLevel,
    #bom_level_2_comps_wo_cns.PerAssemblyQty
INTO #bom_level_2_comps
FROM #bom_level_2_comps_wo_cns
LEFT JOIN #bom_level_1_comps
    ON #bom_level_2_comps_wo_cns.ProductAssemblyID = #bom_level_1_comps.ComponentID
WHERE #bom_level_2_comps_wo_cns.BOMLevel = 2;

-- Zeige die Ergebnisse für Ebene 2
SELECT * FROM #bom_level_2_comps ORDER BY Comp_NodeString;

3. Konvertierung in Hexadezimalformat

SQL Server speichert hierarchyid-Werte intern im Hexadezimalformat, um Leistung und Speicherplatz zu optimieren. Um die Dezimaldarstellung in Hexadezimal umzuwandeln, verwenden wir die Parse-Methode.

Beispiel-Script:

-- Konvertiere NodeStrings in Hexadezimalformat
SELECT 
    hierarchyid::Parse(Ancestor_NodeString) AS Ancestor_Node,
    hierarchyid::Parse(Comp_NodeString) AS Comp_Node,
    hierarchyid::Parse(Comp_NodeString).GetLevel() AS Comp_Node_Level,
    *
INTO #comps_with_nodes
FROM (
    SELECT * FROM #bom_level_1_comps
    UNION
    SELECT * FROM #bom_level_2_comps
) AS CombinedLevels;

-- Zeige die Ergebnisse
SELECT 
    Ancestor_Node,
    Comp_Node,
    Comp_Node_Level,
    assembly_name,
    (SELECT Name FROM Production.Product WHERE ProductID = #comps_with_nodes.ComponentID) AS comp_name,
    BillOfMaterialsID,
    ProductAssemblyID,
    ComponentID,
    UnitMeasureCode,
    BOMLevel,
    PerAssemblyQty
FROM #comps_with_nodes;

Sortierung von BOM-Daten

Breitensuche (Breadth-First)

Bei einer Breitensuche werden alle Elemente einer bestimmten Ebene vor den Elementen der nächsten Ebene angezeigt.

Beispiel-Script:

SELECT 
    Ancestor_Node,
    Comp_Node,
    Comp_Node_Level,
    assembly_name,
    (SELECT Name FROM Production.Product WHERE ProductID = #comps_with_nodes.ComponentID) AS comp_name,
    BillOfMaterialsID,
    ProductAssemblyID,
    ComponentID,
    UnitMeasureCode,
    BOMLevel,
    PerAssemblyQty
FROM #comps_with_nodes
ORDER BY Comp_Node_Level;

Tiefensuche (Depth-First)

Bei einer Tiefensuche werden die Kinder eines Elements vollständig ausgegeben, bevor zum nächsten Geschwisterknoten gewechselt wird.

Beispiel-Script:

SELECT 
    Ancestor_Node,
    Comp_Node,
    Comp_Node_Level,
    assembly_name,
    (SELECT Name FROM Production.Product WHERE ProductID = #comps_with_nodes.ComponentID) AS comp_name,
    BillOfMaterialsID,
    ProductAssemblyID,
    ComponentID,
    UnitMeasureCode,
    BOMLevel,
    PerAssemblyQty
FROM #comps_with_nodes
ORDER BY Comp_Node;

Visualisierung der BOM

Mit den generierten hierarchyid-Werten können Sie auch visuelle Darstellungen der BOM erstellen, z. B. in Excel oder anderen Tools. Ein Beispiel dafür ist die Darstellung der Versammlungshierarchie als Baumdiagramm.

Beispiel:

HL Mountain Frame – Black, 38
├── Chain Stays
│   ├── Metal Sheet 5
│   └── Other Components
├── Fork
│   ├── HL Fork
│   │   ├── Lower Assembly
│   │   └── Crown Race
│   └── Other Components
└── Paint

Fazit

Die Verwendung des hierarchyid-Datentyps in SQL Server vereinfacht die Verwaltung und Abfrage komplexer hierarchischer Strukturen wie Bills of Materials erheblich. Durch die Integration von Methoden wie GetLevel und Parse können Sie nicht nur die Hierarchie effizient speichern, sondern auch schnell und einfach nach bestimmten Kriterien filtern und sortieren.

Um die vorgestellten Konzepte in Ihrer eigenen Organisation umzusetzen, empfehlen wir Ihnen, die Skripts anzupassen und in gespeicherten Prozeduren zu kapseln. Dadurch können Sie die Funktionalität leicht wiederverwendbar machen und Ihre Produktionsprozesse effektiver gestalten.


Nächste Schritte

  1. Laden Sie die AdventureWorks-Datenbank herunter und testen Sie die Skripts in Ihrer lokalen Umgebung.
  2. Passen Sie die Skripts an Ihre eigenen BOM-Daten an.
  3. Erstellen Sie Demonstrationsbeispiele für Ihr Produktions TEAM, um die Vorteile von hierarchyid aufzuzeigen.
  4. Implementieren Sie die Funktionalität in Ihren Produktionsprozessen durch gespeicherte Prozeduren oder andere T-SQL-Container.

Durch die Nutzung von hierarchyid können Sie die Verwaltung Ihrer Materiallisten professionalisieren und die Effizienz Ihrer Produktionsprozesse signifikant steigern.

Window Functions sind ein leistungsstarkes Werkzeug in SQL, das es ermöglicht, Berechnungen über eine Menge von Zeilen durchzuführen, die in irgendeiner Weise mit der aktuellen Zeile zusammenhängen. Diese Funktionen sind besonders nützlich, wenn Sie aggregierte Werte berechnen möchten, ohne die Zeilen in der Ausgabe zu gruppieren. Window Functions werden durch das Vorhandensein einer OVER()-Klausel identifiziert und können in drei Hauptkategorien unterteilt werden: Ranking-Funktionen, Aggregatfunktionen und analytische Funktionen. In diesem Blogbeitrag werden wir jede dieser Kategorien im Detail untersuchen und mit technischen Beispielen illustrieren.


1. Ranking Window Functions

Ranking-Funktionen werden verwendet, um Zeilen innerhalb eines Fensters zu sortieren und zu nummerieren. Sie sind besonders nützlich, um Ranglisten zu erstellen oder Zeilen in bestimmte Gruppen einzuteilen.

1.1 ROW_NUMBER()

Die ROW_NUMBER()-Funktion weist jeder Zeile innerhalb des Fensters eine eindeutige Nummer zu, beginnend bei 1. Die Reihenfolge wird durch die ORDER BY-Klausel innerhalb der OVER()-Klausel bestimmt.

Beispiel:

SELECT 
    MitarbeiterID, 
    Gehalt,
    ROW_NUMBER() OVER (ORDER BY Gehalt DESC) AS Rang
FROM 
    Mitarbeiter;

Erklärung: Dieses Query gibt eine Liste der Mitarbeiter zurück, sortiert nach ihrem Gehalt in absteigender Reihenfolge. Jeder Mitarbeiter erhält eine eindeutige Rangnummer basierend auf seinem Gehalt.


1.2 RANK()

Die RANK()-Funktion weist jeder Zeile einen Rang zu, wobei Zeilen mit denselben Werten denselben Rang erhalten. Es gibt jedoch Lücken in der Rangfolge, wenn mehrere Zeilen denselben Rang haben.

Beispiel:

SELECT 
    MitarbeiterID, 
    Gehalt,
    RANK() OVER (ORDER BY Gehalt DESC) AS Rang
FROM 
    Mitarbeiter;

Erklärung: Wenn zwei Mitarbeiter das gleiche Gehalt haben, erhalten sie denselben Rang. Der nächste Mitarbeiter erhält dann den Rang, der der Anzahl der vorherigen Zeilen entspricht, was zu Lücken in der Rangfolge führen kann.


1.3 DENSE_RANK()

Die DENSE_RANK()-Funktion ähnelt RANK(), jedoch ohne Lücken in der Rangfolge. Wenn mehrere Zeilen denselben Rang haben, erhält die nächste Zeile den nächsten Rang ohne Überspringung.

Beispiel:

SELECT 
    MitarbeiterID, 
    Gehalt,
    DENSE_RANK() OVER (ORDER BY Gehalt DESC) AS Rang
FROM 
    Mitarbeiter;

Erklärung: Auch hier erhalten Mitarbeiter mit demselben Gehalt denselben Rang, aber es gibt keine Lücken in der Rangfolge.


1.4 NTILE()

Die NTILE()-Funktion teilt die Zeilen in eine angegebene Anzahl von Gruppen (Buckets) ein. Jeder Zeile wird eine Gruppennummer zugewiesen.

Beispiel:

SELECT 
    MitarbeiterID, 
    Gehalt,
    NTILE(4) OVER (ORDER BY Gehalt DESC) AS Quartil
FROM 
    Mitarbeiter;

Erklärung: Dieses Query teilt die Mitarbeiter in vier gleich große Gruppen (Quartile) basierend auf ihrem Gehalt ein.


2. Aggregate Window Functions

Aggregatfunktionen können auch als Window Functions verwendet werden, um aggregierte Werte über ein Fenster von Zeilen zu berechnen, ohne die Zeilen zu gruppieren.

2.1 MIN(), MAX(), AVG(), SUM()

Diese Funktionen berechnen den minimalen, maximalen, durchschnittlichen oder summierten Wert über ein Fenster von Zeilen.

Beispiel:

SELECT 
    MitarbeiterID, 
    Gehalt,
    AVG(Gehalt) OVER (PARTITION BY AbteilungID) AS Durchschnittsgehalt
FROM 
    Mitarbeiter;

Erklärung: Dieses Query berechnet das durchschnittliche Gehalt für jede Abteilung (AbteilungID) und zeigt es neben dem Gehalt jedes Mitarbeiters an.


2.2 COUNT(), COUNT_BIG()

Die COUNT()-Funktion zählt die Anzahl der Zeilen im Fenster, während COUNT_BIG() für große Datensätze verwendet wird.

Beispiel:

SELECT 
    AbteilungID, 
    COUNT(MitarbeiterID) OVER (PARTITION BY AbteilungID) AS Mitarbeiteranzahl
FROM 
    Mitarbeiter;

Erklärung: Dieses Query zählt die Anzahl der Mitarbeiter in jeder Abteilung.


2.3 STDEV(), STDEVP(), VAR(), VARP()

Diese Funktionen berechnen die Standardabweichung und Varianz über ein Fenster von Zeilen.

Beispiel:

SELECT 
    AbteilungID, 
    STDEV(Gehalt) OVER (PARTITION BY AbteilungID) AS Gehaltsstandardabweichung
FROM 
    Mitarbeiter;

Erklärung: Dieses Query berechnet die Standardabweichung der Gehälter in jeder Abteilung.


3. Analytic Window Functions

Analytische Funktionen ermöglichen es, komplexe Berechnungen über ein Fenster von Zeilen durchzuführen, z. B. den Zugriff auf vorherige oder nachfolgende Zeilen.

3.1 LAG() und LEAD()

Die LAG()-Funktion ermöglicht den Zugriff auf eine vorherige Zeile, während LEAD() den Zugriff auf eine nachfolgende Zeile ermöglicht.

Beispiel:

SELECT 
    MitarbeiterID, 
    Gehalt,
    LAG(Gehalt, 1) OVER (ORDER BY Gehalt) AS VorherigesGehalt,
    LEAD(Gehalt, 1) OVER (ORDER BY Gehalt) AS NaechstesGehalt
FROM 
    Mitarbeiter;

Erklärung: Dieses Query zeigt das Gehalt des vorherigen und des nächsten Mitarbeiters in der sortierten Liste an.


3.2 FIRST_VALUE() und LAST_VALUE()

Diese Funktionen geben den ersten bzw. letzten Wert im Fenster zurück.

Beispiel:

SELECT 
    MitarbeiterID, 
    Gehalt,
    FIRST_VALUE(Gehalt) OVER (ORDER BY Gehalt) AS NiedrigstesGehalt,
    LAST_VALUE(Gehalt) OVER (ORDER BY Gehalt ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS HoechstesGehalt
FROM 
    Mitarbeiter;

Erklärung: Dieses Query zeigt das niedrigste und höchste Gehalt in der gesamten Tabelle an.


3.3 PERCENT_RANK(), PERCENTILE_CONT(), PERCENTILE_DISC()

Diese Funktionen berechnen den prozentualen Rang oder den Wert an einem bestimmten Perzentil.

Beispiel:

SELECT 
    MitarbeiterID, 
    Gehalt,
    PERCENT_RANK() OVER (ORDER BY Gehalt) AS ProzentRang,
    PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY Gehalt) OVER () AS MedianGehalt
FROM 
    Mitarbeiter;

Erklärung: Dieses Query berechnet den prozentualen Rang jedes Gehalts und den Median des Gehalts.


3.4 CUME_DIST()

Die CUME_DIST()-Funktion berechnet die kumulative Verteilung eines Werts innerhalb eines Fensters.

Beispiel:

SELECT 
    MitarbeiterID, 
    Gehalt,
    CUME_DIST() OVER (ORDER BY Gehalt) AS KumulativeVerteilung
FROM 
    Mitarbeiter;

Erklärung: Dieses Query zeigt die kumulative Verteilung der Gehälter an, d. h. den Anteil der Gehälter, die kleiner oder gleich dem aktuellen Gehalt sind.


Fazit

Window Functions sind ein äußerst nützliches Werkzeug in SQL, das es ermöglicht, komplexe Berechnungen über Fenster von Zeilen durchzuführen, ohne die Daten gruppieren zu müssen. Durch die Verwendung von Ranking-, Aggregat- und analytischen Funktionen können Sie leistungsstarke Abfragen erstellen, die tiefe Einblicke in Ihre Daten liefern. Mit den oben genannten Beispielen sollten Sie in der Lage sein, Window Functions in Ihren eigenen SQL-Abfragen effektiv einzusetzen.

Liste von Testdatenbanken und Skripten

- Veröffentlicht unter SQL & Datenbanken von

Hier ist eine Liste von Testdatenbanken und Skripten, die für SQL Server nützlich sein können, um verschiedene Szenarien zu testen, Leistungsanalysen durchzuführen oder Schulungen zu ermöglichen:


Testdatenbanken

  1. AdventureWorks

    • Beschreibung: Eine weit verbreitete Beispiel-Datenbank von Microsoft, die für Schulungen und Tests verwendet wird. Sie enthält Daten für fiktive Unternehmen in den Bereichen Produktion, Vertrieb und Personalwesen.
    • Versionen: AdventureWorksLT (Lightweight), AdventureWorksDW (Data Warehouse), AdventureWorks (OLTP).
    • Download: Verfügbar auf GitHub oder über Microsoft Docs.
  2. Northwind

    • Beschreibung: Eine klassische Beispiel-Datenbank, die ursprünglich für Microsoft Access entwickelt wurde. Sie enthält Daten zu Bestellungen, Produkten, Kunden und Lieferanten.
    • Verwendung: Ideal für einfache SQL-Abfragen und grundlegende Datenbankoperationen.
    • Download: Verfügbar auf GitHub.
  3. Wide World Importers

    • Beschreibung: Eine moderne Beispiel-Datenbank, die von Microsoft entwickelt wurde, um aktuelle SQL Server-Funktionen wie JSON, Temporal Tables und Columnstore-Indizes zu demonstrieren.
    • Versionen: WideWorldImporters (OLTP) und WideWorldImportersDW (Data Warehouse).
    • Download: Verfügbar auf GitHub.
  4. Stack Overflow Database

    • Beschreibung: Ein Extrakt der öffentlichen Daten von Stack Overflow, der für Leistungstests und komplexe Abfragen verwendet wird. Die Datenbank ist groß und realistisch.
    • Download: Verfügbar auf Brent Ozar's Website.
  5. Chinook Database

    • Beschreibung: Eine plattformübergreifende Beispiel-Datenbank, die Musikdaten enthält (z. B. Künstler, Alben, Tracks). Sie ist einfach zu verwenden und gut dokumentiert.
    • Download: Verfügbar auf GitHub.
  6. Contoso Retail

    • Beschreibung: Eine Beispiel-Datenbank für Einzelhandelsdaten, die für Data Warehousing und BI-Tests geeignet ist.
    • Download: Verfügbar auf Microsoft Docs.

Testskripte

  1. SQL Server Sample Scripts

    • Beschreibung: Offizielle Skripte von Microsoft, die verschiedene SQL Server-Funktionen demonstrieren, einschließlich Sicherheit, Leistungsoptimierung und Verwaltung.
    • Download: Verfügbar auf GitHub.
  2. sp_WhoIsActive

    • Beschreibung: Ein nützliches gespeichertes Verfahren von Adam Machanic, um aktive Abfragen und Prozesse auf einem SQL Server zu überwachen.
    • Download: Verfügbar auf WhoIsActive.com.
  3. SQLQueryStress

    • Beschreibung: Ein Tool von Adam Machanic, um SQL-Abfragen unter Last zu testen und die Leistung zu analysieren.
    • Download: Verfügbar auf GitHub.
  4. DBCC CHECKDB Scripts

    • Beschreibung: Skripte zur Überprüfung der Datenbankintegrität und zur Fehlerbehebung.
    • Verwendung: Nützlich für Wartungs- und Diagnosezwecke.
    • Beispiel:
      sql DBCC CHECKDB('YourDatabaseName') WITH NO_INFOMSGS, ALL_ERRORMSGS;
  5. Generate Test Data Scripts

    • Beschreibung: Skripte zur Generierung von Testdaten für Tabellen, z. B. zufällige Namen, Adressen oder Zahlen.
    • Beispiel:
      sql DECLARE @i INT = 1; WHILE @i <= 1000 BEGIN INSERT INTO TestTable (Name, Age) VALUES ('User' + CAST(@i AS VARCHAR), RAND() * 100); SET @i = @i + 1; END;
  6. Index Optimization Scripts

    • Beschreibung: Skripte zur Analyse und Optimierung von Indizes, z. B. zur Identifizierung fehlender oder überflüssiger Indizes.
    • Beispiel:
      sql SELECT * FROM sys.dm_db_missing_index_details;
  7. Performance Monitoring Scripts

    • Beschreibung: Skripte zur Überwachung der Serverleistung, z. B. CPU-Auslastung, Speicherverbrauch und E/A-Statistiken.
    • Beispiel:
      sql SELECT * FROM sys.dm_os_performance_counters;
  8. Backup and Restore Test Scripts

    • Beschreibung: Skripte zum Testen von Backup- und Wiederherstellungsprozessen.
    • Beispiel:
      sql BACKUP DATABASE YourDatabaseName TO DISK = 'C:\Backup\YourDatabaseName.bak'; RESTORE DATABASE YourDatabaseName FROM DISK = 'C:\Backup\YourDatabaseName.bak';

Zusätzliche Ressourcen

  • SQL Server Management Studio (SSMS): Enthält integrierte Beispiele und Vorlagen für Skripte.
  • SQL Server Data Tools (SSDT): Nützlich für die Entwicklung und das Testen von Datenbankprojekten.
  • Online Generatoren: Tools wie Mockaroo können verwendet werden, um realistische Testdaten zu generieren.

Diese Ressourcen sind ideal für Entwickler, Datenbankadministratoren und Datenanalysten, um SQL Server-Umgebungen zu testen und zu optimieren.

Blog-Beitrag: DuckDB – Die revolutionäre Datenbank für analytische Workloads


Einleitung

In der Welt der Datenbanken gibt es immer wieder neue Tools, die versprechen, unsere Arbeit effizienter und einfacher zu machen. Eines dieser Tools, das in letzter Zeit viel Aufmerksamkeit auf sich gezogen hat, ist DuckDB. DuckDB ist eine in-process SQL-Datenbank, die speziell für analytische Workloads entwickelt wurde. In diesem Beitrag werfen wir einen Blick darauf, was DuckDB so besonders macht, warum es sich lohnt, es auszuprobieren, und wie du es in deinen Projekten einsetzen kannst.


Was ist DuckDB?

DuckDB ist eine Open-Source-Datenbank, die für Datenanalyse optimiert ist. Im Gegensatz zu herkömmlichen Datenbanken wie MySQL oder PostgreSQL ist DuckDB in-process, was bedeutet, dass sie direkt in deiner Anwendung läuft – ohne separaten Server. Das macht sie besonders schnell und einfach zu nutzen.

DuckDB ist ideal für:
- Analytische Abfragen (OLAP – Online Analytical Processing)
- Datenwissenschaftliche Workloads
- Lokale Datenanalyse ohne komplexe Infrastruktur


Warum DuckDB?

Hier sind einige Gründe, warum DuckDB eine Überlegung wert ist:

  1. Einfache Installation und Nutzung:
    DuckDB lässt sich leicht in Python, R oder anderen Sprachen integrieren. Es gibt keine aufwendige Server-Konfiguration – einfach installieren und loslegen.

  2. Blitzschnelle Abfragen:
    DuckDB ist für analytische Abfragen optimiert und nutzt moderne Techniken wie Vektorisierung, um Daten schnell zu verarbeiten.

  3. Keine externe Infrastruktur nötig:
    Da DuckDB in-process läuft, brauchst du keinen separaten Datenbankserver. Das macht es perfekt für lokale Analysen oder Embedded-Systeme.

  4. SQL-Unterstützung:
    DuckDB unterstützt standardisiertes SQL, sodass du keine neue Sprache lernen musst.

  5. Open Source:
    DuckDB ist kostenlos und quelloffen, mit einer aktiven Community, die ständig neue Features entwickelt.


DuckDB in Aktion: Ein Beispiel mit Python

Lass uns DuckDB mit Python ausprobieren. Wir werden eine CSV-Datei laden, eine einfache Abfrage durchführen und die Ergebnisse anzeigen.

Schritt 1: Installation

Installiere DuckDB mit pip:

pip install duckdb
Schritt 2: CSV-Datei laden und analysieren

Angenommen, du hast eine CSV-Datei mit Verkaufsdaten (sales.csv). So kannst du sie mit DuckDB analysieren:

import duckdb

# Verbindung zur DuckDB-Datenbank herstellen (in-memory)
con = duckdb.connect(database=':memory:')

# CSV-Datei laden
con.execute("CREATE TABLE sales AS SELECT * FROM read_csv_auto('sales.csv')")

# Eine einfache Abfrage durchführen
result = con.execute("SELECT product, SUM(revenue) AS total_revenue FROM sales GROUP BY product").fetchdf()

# Ergebnisse anzeigen
print(result)
Ergebnis:

Du erhältst eine Tabelle mit den Gesamtumsätzen pro Produkt – schnell und ohne großen Aufwand!


Wann sollte man DuckDB verwenden?

DuckDB eignet sich besonders gut für:
- Lokale Datenanalyse: Wenn du schnell Daten analysieren möchtest, ohne eine komplexe Infrastruktur aufzubauen.
- Embedded-Systeme: Wenn du eine Datenbank in deiner Anwendung einbetten möchtest.
- Prototyping: Für schnelle Tests und Experimente mit Daten.

Für transaktionale Workloads (OLTP – Online Transaction Processing) oder sehr große Datenmengen, die auf verteilten Systemen laufen müssen, sind jedoch andere Datenbanken wie PostgreSQL oder Big-Data-Tools wie Apache Spark besser geeignet.


Fazit

DuckDB ist ein leistungsstarkes Werkzeug für analytische Workloads, das durch seine Einfachheit und Geschwindigkeit überzeugt. Ob du Datenwissenschaftler, Analyst oder Entwickler bist – DuckDB kann dir helfen, deine Daten schneller und effizienter zu analysieren.

Probiere es aus und lass uns wissen, wie es dir gefällt! Hast du schon Erfahrungen mit DuckDB gemacht? Teile sie gerne in den Kommentaren.


Weiterführende Ressourcen


Das war’s für heute! Bleib dran für mehr Beiträge zu spannenden Tools und Technologien im Bereich Daten, SQL, Python und KI. 😊