Microsoft Structured Query Language
Mit JOIN
kann die ursprüngliche Tabelle von FROM
mit einer zweiten Tabelle verbunden werden.
Die Verbindung selbst wird mit ON
definiert.
Name | CategoryID | RatingID |
---|---|---|
Gummibärchen | 1 | 1 |
Pommes Chips | 1 | 1 |
Apfel | 2 | null |
Kiwi | 2 | 1 |
Banane | 2 | 2 |
Curry | null | 3 |
Essig | null | null |
ID | Name | GroupID |
---|---|---|
1 | ungesund | 1 |
2 | gesund | 2 |
3 | giftig | null |
ID | Description |
---|---|
1 | habe ich gern |
2 | habe ich nicht gern |
3 | habe ich noch nie probiert |
ID | Description |
---|---|
1 | essbar |
2 | spuckbar |
3 | würgbar |
Im Standard werden nur Zeilen ausgegeben, wo die Verbindung hergestellt werden konnte.
FROM Items i
JOIN Categories c ON i.CategoryID = c.ID
Das Ergebniss beinhaltet also weder die Kategorie "giftig" noch die Gegenstände "Curry" und "Essig".
Hier werden alle Zeilen aus der bestehenden Tabelle ausgegeben und, wenn vorhanden, mit der zweiten Tabelle ergänzt.
FROM Items i
LEFT JOIN Categories c ON i.CategoryID = c.ID
"Curry" und "Essig" werden also trotzdem angezeigt, einfach ohne die Werte aus der Kategorie-Tabelle. Jedoch die Kategorie "giftig" wird nicht angezeigt.
Hier werden alle Zeilen aus der zweiten Tabelle ausgegeben und, wenn vorhanden, mit der bestehenden Tabelle ergänzt.
FROM Items i
RIGHT JOIN Categories c ON i.CategoryID = c.ID
Im Ergebniss findet man die Kategorie "giftig", jedoch ohne einen Gegenstand, während "Curry" und "Essig" fehlen.
Damit werden alle Zeilen aus beiden Tabellen ausgegeben.
FROM Items i
FULL JOIN Categories c ON i.CategoryID = c.ID
Das Ergebniss beinhaltet alle Zeilen und wo vorhanden, auch mit den Infos der jeweils anderen Tabelle befüllt.
Wenn mehrere Joins angewendet werden, dann ist die Reihenfolge, der verbundenen Tabelle relevant. Es gilt bei jedem Join wieder: linke Tabelle = aktuelle Tabelle, rechte Tabelle = neue Tabelle.
FROM Items i
LEFT JOIN Categories c ON i.CategoryID = c.ID
LEFT JOIN Groups g ON c.GroupID = g.ID
LEFT JOIN Ratings r ON i.RatingID = r.ID
Hier werden zuerst die Kategorien verbunden. Dann werden die Gruppen geladen und am Ende noch die Bewertungen.
FROM Items i
LEFT JOIN Categories c ON i.CategoryID = c.ID
RIGHT JOIN Groups g ON c.GroupID = g.ID
FULL JOIN Ratings r ON i.RatingID = r.ID
In diesem Beispiel werden zuerst zu allen Gegenständen die Kategorie geladen.
Da dies mit LEFT JOIN
geschieht, werden auch "Curry" und "Essig" mit der Kategorie "null" ausgegeben.
Danach wird mit RIGHT JOIN
die Gruppe dazugeholt.
Deshalb entfallen hier "Curry" und "Essig" wieder, da ihre Kategorie "null" ist und folgend auch die Gruppe "null" ist.
Dafür kommt eine Zeile für die Gruppe "würgbar" dazu, welche weder einen Gegenstand noch eine Kategorie hat.
Im letzten Schritt kommt die Tabelle Bewertung anhand FULL JOIN
dazu und bringt eine neue Zeile mit "habe ich noch nie probiert", welche alle restlichen Felder leer lässt.
FROM Items i
FULL JOIN Ratings r ON i.RatingID = r.ID
LEFT JOIN Categories c ON i.CategoryID = c.ID
RIGHT JOIN Groups g ON c.GroupID = g.ID
In diesem Beispiel laden wir die Bewertung vor der Kategorie und der Gruppe.
Dies verdeutlicht, dass die Reihenfolge relevant ist.
Zuerst werden die Gegenstände mit der Bewertung anhand eines FULL JOIN
geladen.
"Apfel" und "Essig" haben keine Bewertung und werden mit "null" dazugeladen.
Von den Bewertungen werden alle drei Stück verwendet, weshalb es keine weitere Zeile gibt.
Nun wird mit einem LEFT JOIN
die Kategorie dazugeholt, welche die Anzahl der Zeilen unverändert lässt.
"Curry" und "Essig" bleiben mit einer "null" darin bestehen.
Mit einem RIGHT JOIN
wird nun die Gruppe geladen.
Da "Curry" und "Essig" keine Gruppe besitzen, fallen sie weg.
Dafür wird die nicht verwendete Gruppe "würgbar" hinzugefügt.
Möchte man nur die ersten 10 Zeilen der Abfrage ausgegeben erhalten, kann dies einfach mit einem TOP 10
geschehen:
SELECT TOP 10 * FROM table ORDER BY name
Möchte man jedoch eine Funktion zum Blättern, ist das schon umständlicher. Dafür muss man dies nicht ganz so intuitiv am Ende der Abfrage hinzufügen:
SELECT *
FROM table
ORDER BY name
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY
Man kann im SQL mittels WHILE
Schleifen nutzen.
Selbstverständlich kann man in der WHILE
-Bedinung alle gültigen Vergleiche anstellen.
Das folgende Beispiel zeigt, wie man auch eine FOR
-Schleife bauen kann.
Den nächsten Schleifendurchlauf lässt sich mittels CONTINUE
einleiten.
DECLARE @i int = 0
WHILE @i < 10
BEGIN
SET @i = @i + 1
IF @i = 6 CONTINUE
PRINT CONCAT(@i, '. Iteration')
END
MSSQL kann auch mit Json umgehen.
Ein Json-Objekt kann mit OPENJSON
gelesen werden.
DECLARE @json nvarchar(max) = '[{"Gegenstand": "Gummibärchen", "Bestand": 31},{"Gegenstand": "Pommes Chips", "Bestand": 13}]'
SELECT * FROM OPENJSON(@json) WITH
(
Gegenstand nvarchar(max) 'strict $.Gegenstand',
Bestand int 'strict $.Bestand'
)
Auch lässt sich mittels OPENXML
dieses alte Format lesen.
Man kann mehrere Zeilen auslesen, wie auch Felderinhalte, -Bezeichnungen und -Attribute.
DECLARE @xml nvarchar(max) = '<gegenstaende><gegenstand order="1" added="20240204"><name>Apfel</name><bestand>7</bestand></gegenstand><gegenstand order="2"><name>Banane</name><bestand>3</bestand></gegenstand></gegenstaende>'
DECLARE @i int
EXEC sp_xml_preparedocument @i out, @xml, '<row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />'
SELECT * FROM OPENXML(@i, '/gegenstaende/*', 1) WITH
(
Gegenstand nvarchar(8) 'name',
Bestand int 'bestand',
Feld nvarchar(16) '@mp:localname',
Inhalt nvarchar(16) '.',
Reihenfolge int '@order'
)
EXEC sp_xml_removedocument @i
Mit XQuery lassen sich noch einfacher XML-Daten verarbeiten.
DECLARE @essen table (Daten xml)
INSERT INTO @essen VALUES
('<essen id="1"><name>Gummibärchen</name><kategorie>ungesund</kategorie><gruppe>essbar</gruppe><bewertung>habe ich gern</bewertung></essen>'),
('<essen id="2"><name>Kiwi </name><kategorie>gesund</kategorie><gruppe>spuckbar</gruppe><bewertung>habe ich gern</bewertung></essen>'),
('<essen id="3"><name>Rattengift</name><kategorie>giftig</kategorie><gruppe>würgbar</gruppe><bewertung>habe ich noch nie probiert</bewertung></essen>')
SELECT
Daten.value('(essen/@id)[1]', 'int') as ID,
Daten.value('(essen/name)[1]', 'nvarchar(16)') as Gegenstand,
Daten.value('(essen/kategorie)[1]', 'nvarchar(16)') as Kategorie,
Daten.value('(essen/gruppe)[1]', 'nvarchar(16)') as Gruppe,
Daten.value('(essen/bewertung)[1]', 'nvarchar(32)') as Bewertung
FROM @essen
Mit gegenstand[@order="1"]/@added
kann man gezielt eine Node nach einem Attribut selektieren und darin ein anderes Attribut ausgeben lassen.
Hier wäre das Resultat 20240204.
Ein Index ist eine interne "Merkliste" der Datenbank, um schneller den entsprechenden Datensatz im Speicher zu finden. Werden Datensätze entfernt und hinzugefügt, wird der entsprechende Index-Eintrag ebenfalls gelöscht bzw. hinzugefügt. Je öfters dies stattfindet, desto schneller fragmentiert der Index. Mit diesem Skript, lässt sich die Fragmentierung auslesen und optimieren, in dem er neu erstellt wird.
BEGIN TRANSACTION
DECLARE @overview as table
(
[Row] int,
[Schema] nvarchar(128),
[Table] nvarchar(128),
[Index] nvarchar(128),
[Fragmentation] float
)
INSERT INTO @overview
SELECT
ROW_NUMBER() OVER (ORDER BY d.avg_fragmentation_in_percent),
s.name as [Schema],
t.name as [Table],
i.name as [Index],
d.avg_fragmentation_in_percent
FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL, NULL, NULL) AS d
JOIN sys.tables t on T.object_id = d.object_id
JOIN sys.schemas s on T.schema_id = s.schema_id
JOIN sys.indexes i ON I.object_id = d.object_id AND d.index_id = i.index_id
WHERE d.database_id = DB_ID()
AND i.name IS NOT NULL
AND d.avg_fragmentation_in_percent > 0
DECLARE @i int = 0
DECLARE @max int = (SELECT MAX([row]) FROM @overview)
DECLARE @sql nvarchar(max)
WHILE @i < @max
BEGIN
SET @i = @i + 1
SET @sql =
(
SELECT CONCAT('ALTER INDEX [', [Index], '] ON ', [Table], ' REBUILD')
FROM @overview WHERE [row] = @i
)
EXEC(@sql)
END
--SELECT * FROM @overview ORDER BY [Row]
COMMIT TRANSACTION