T-SQL Ninja #18

WHILE

Was ist WHILE?

Im Gegensatz zu Data Definition Language Statements zum Erstellen von Views, Tabellen oder Indexen, Abfrage-Statements, um Daten in diese Strukturen einzufügen und wieder abzurufen, ist das WHILE-Schlüsselwort teil der Ablaufsteuerung. Das heißt, mit diesem Schlüsselwort können in SQL Statements Abläufe gesteuert und bestimmte Abfragen mehrfach ausgeführt werden.

Wie könnt ihr WHILE Statements verwenden?

WHILE-Statements sind vor allem dann nützlich, wenn ihr eine Aufgabe mehrfach ausführen möchtet, also so lange bis eine bestimmte Bedingung erfüllt ist. Um zu demonstrieren, wie das funktioniert, legen wir uns zunächst eine kleine Tabelle an, in der wir festhalten, wie tödlich verschiedene Ninjas unseres Dojo sind:

DROP TABLE IF EXISTS #NinjaDemo

CREATE TABLE #NinjaDemo
(
     NinjaId INT IDENTITY(1,1)
    ,Deadlyness float NOT NULL
)

Nun möchten wir diese Tabelle mit den ersten fünf Ninjas befüllen, da wir aber nur Demo-Datensätze verwenden, um die Identität unserer Ninjas zu schützen, füllen wir die Tödlichkeits-Spalte mit Zufallszahlen. Das können wir dann wie folgt tun:

INSERT INTO #NinjaDemo (Deadlyness)
VALUES
 (RAND())
,(RAND())
,(RAND())
,(RAND())
,(RAND())

Sehen wir uns die Datensätze nun an, so sehen wir fünf Einträge mit zufälligen Werten für Deadlyness. Wenn wir das nun aber für ein größeres Dojo tun möchten und statt der fünf Ninjas, 200 Einträge benötigen, könnten wir entweder per Copy & Paste ein sehr langes (und unübersichtliches) Statement erzeugen oder aber wir könnten ein WHILE-Statement verwenden, um die Datensätze zu erzeugen. Das ginge dann wie folgt:

DECLARE @nRows int = 0;

SELECT @nRows = COUNT(*) FROM #NinjaDemo
WHILE @nRows < 200
BEGIN
    INSERT INTO #NinjaDemo (Deadlyness) VALUES (RAND())
    SELECT @nRows = COUNT(*) FROM #NinjaDemo
END

SELECT * FROM #NinjaDemo

Nun hat die Tabelle 200 Einträge, wenn wir stattdessen aber 500 oder 5000 Einträge möchten, müssten wir in diesem Statement nur eine Zahl in der WHILE-Bedingung ändern. Doch große Tabellen mit Zufallszahlen zu füllen, dafür würde sich ein eigenes Sprachkonstrukt sicher nicht lohnen. Das WHILE-Statement kann natürlich noch mehr als das. Angenommen wir möchten alle geclusterten Indexe in unserer Datenbank neu aufbauen. Schreiben wir uns dafür zunächst eine Abfrage, die alle geclusterten Indexe auflistet:

SELECT 
    i.[name] as index_name
    ,schema_name(t.schema_id) + '.' + t.[name] as table_view
FROM sys.objects t
INNER JOIN sys.indexes i
    ON t.object_id = i.object_id
WHERE t.[type] = 'U'
    AND i.[type] = 1

Um nun alle diese Indexe neu aufzubauen, generieren wir uns dynamische SQL Statements, die wir ausführen möchten – ACHTUNG, das solltet Ihr natürlich im Dojo ausprobieren bevor ihr damit beim echten Wettkampf antretet (oder deutsch: nicht auf Produktivservern ausführen!). Das können wir mit einem WHILE-Statement tun, indem wir immer einen Index neu aufbauen bis kein Index mehr aufzubereiten ist. Dafür erzeugen wir uns zunächst eine temporäre Tabelle mit den Indexen, die wir neu aufbauen möchten:

DROP TABLE IF EXISTS #sqlReorg

SELECT 
    'ALTER INDEX ['+i.[name]+'] ON ['+schema_name(t.schema_id) + '].[' + t.[name]+'] REORGANIZE ;' as Stmt
INTO #sqlReorg FROM sys.objects t
INNER JOIN sys.indexes i
    ON t.object_id = i.object_id
WHERE t.[type] = 'U'
    AND i.[type] = 1

Nun holen wir uns aus dieser Tabelle immer einen Index, generieren das zugehörige Statement und löschen den Index aus der Tabelle:

DECLARE @stmt NVARCHAR(500)
WHILE (SELECT COUNT(*) FROM #sqlReorg) > 0
BEGIN
    SELECT top(1) @stmt = [stmt] FROM #sqlReorg
    DELETE FROM #sqlReorg WHERE [Stmt] = @stmt
    PRINT 'Running '+@stmt
    EXEC sp_Executesql @stmt
END

Das ist natürlich so nicht best practice, da hier beispielsweise komplett auf Fehlerbehandlung verzichtet wurde, es zeigt aber, wie das WHILE-Statement verwendet werden kann, um mehrere Statements auszuführen. Wichtig ist: SQL ist immer dann effizient, wenn mit Datenmengen gearbeitet wird. WHILE-Statements arbeiten per Definition zunächst mit einzelnen Datensätzen, das bedeutet was ihr hier tut ist potenziell immer eine langsame Lösung für Euer Problem. Oft gibt es bessere und effizientere Lösungen, bevor ihr also mit einem WHILE-Statement an den Start geht, überlegt gut, ob das wirklich die beste Lösung ist und ob ihr es dem Server nicht doch irgendwie ermöglichen könnt, effizient mit mehreren Datensätzen zu arbeiten…

Referenzen

Ninja-Notebooks @ GitHub

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.