SQL Datenbank Performance Optimierungen
Bei älteren OCT Versionen vor 5.10 und längerfristigem Betrieb von OCT kann es zu Performanceproblemen der SQL Server Datenbank kommen. Die folgenden Abfragen sollen eine Hilfestellung zur Erkennung und Verbesserung der Ursachen geben.
Alle Tabellen mit verwendetem Speicherplatz auflisten
SELECT 
    s.Name AS "Schema",
    t.Name AS Tabelle,
    p.Rows AS Zeilen,
    SUM(a.total_pages) * 8 / 1024 AS SpeicherplatzMB,
    SUM(a.used_pages) * 8 / 1024 AS GenutzterSpeicherplatzMB, 
    (SUM(a.total_pages) - SUM(a.used_pages)) * 8 / 1024 AS UngenutzterSpeicherplatzMB
FROM sys.tables t
INNER JOIN sys.indexes          AS i
ON t.object_id = i.object_id
INNER JOIN sys.partitions       AS p
ON i.object_id = p.object_id AND i.index_id = p.index_id
INNER JOIN sys.allocation_units AS a
ON p.partition_id = a.container_id
INNER JOIN sys.schemas          AS s
ON t.schema_id = s.schema_id
WHERE i.index_id <= 1
GROUP BY t.Name, s.Name, p.Rows
ORDER BY SpeicherplatzMB DESC;Diese Abfrage liefert eine Liste von allen Tabellen mit folgenden Informationen:
- Anzahl an Zeilen 
- gesamt belegter Speicherplatz in MB 
- genutzter Speicherplatz in MB 
- ungenutzter Speicherplatz in MB 
Folgende Umstände könnten ein Indikator für Performanceprobleme sein:
- ungewöhnlich hohe Werte in der Spalte “UngenutzterSpeicherplatzMB” 
- verhältnismäßig hoher Wert bei “SpeicherplatzMB” für die Anzahl der Zeilen 
Nicht mehr benötigte Daten löschen
-- API Log bereinigen
IF OBJECT_ID('planning.tAPI_Log') IS NOT NULL
  TRUNCATE TABLE planning.tAPI_Log
IF OBJECT_ID('system.tAPI_Log') IS NOT NULL
  TRUNCATE TABLE system.tAPI_Log
-- Pipeline Log bereinigen
DELETE FROM system.tPipelineStepLog WHERE YEAR(StartTime) < YEAR(GETDATE())
DELETE From system.tPipelineLog WHERE YEAR(StartTime) < YEAR(GETDATE())
DELETE FROM system.tProcessLogs WHERE YEAR(Timestamp) < YEAR(GETDATE())Diese SQL Befehlen löschen alle Daten aus dem API Log und alle Pipeline Log Einträge aus vorherigen Jahren. Bei längerfristigem Betrieb von OCT und vielen Pipeline-Ausführungen sind die angesprochenen Tabellen üblicherweise sehr groß und belegen viel Speicherplatz.
Überflüssige Indizes entfernen
DROP INDEX IF EXISTS icc_global_tFIN_Accounts ON global.tFIN_Accounts
DROP INDEX IF EXISTS icc_global_tFIN_CostObjects ON global.tFIN_CostObjectsIn älteren Versionen wurde auf diversen global-Tabellen ein unnötiger Index erstellt, der gelöscht werden kann.
Tabellen ohne gruppierten Index
SELECT 
    s.Name as "Schema",
    t.Name AS Tabelle,
    i.Name AS IndexName,
    i.type_desc AS IndexTyp
FROM sys.tables  AS t
JOIN sys.indexes AS i
ON t.object_id = i.object_id
JOIN sys.schemas AS s
ON s.schema_id = t.schema_id
WHERE EXISTS(
    SELECT 1
    FROM sys.tables  AS t2
    JOIN sys.indexes AS i2
    ON t.object_id = i2.object_id
    WHERE
        i2.type_desc = 'HEAP'
        and t2.object_id = t.object_id
)
AND i.Name IS NOT NULL
ORDER BY "Schema", TabelleDiese Abfrage liefert eine Liste von allen Tabellen, die keinen gruppierten Index haben. Üblicherweise sind die auffälligen Tabellen aus der ersten Abfrage auch in dieser Liste zu finden.
Ein gruppierter Index sorgt für eine organisierte physische Speicherung der Daten auf der Festplatte. Vor allem bei größeren Tabellen kann ein fehlender gruppierter Index zu großen Performanceproblemen beim Lesen & Schreiben führen. Dementsprechend sollte jede Tabelle einen gruppierten Index haben.
Fehlende gruppierte Indizes erstellen
DECLARE @SchemaName NVARCHAR(255)
DECLARE @TableName NVARCHAR(255)
DECLARE @IndexName NVARCHAR(255)
DECLARE @SQL NVARCHAR(MAX)
DECLARE cur CURSOR FOR
SELECT
    s.Name,
    t.Name,
    i.Name
FROM sys.tables  AS t
JOIN sys.indexes AS i
ON t.object_id = i.object_id
JOIN sys.schemas AS s
ON s.schema_id = t.schema_id
WHERE EXISTS (
    SELECT 1
    FROM sys.tables AS t2
    JOIN sys.indexes AS i2
    ON t.object_id = i2.object_id
    WHERE
        i2.type_desc = 'HEAP'
        AND t2.object_id = t.object_id
)
AND i.type_desc = 'NONCLUSTERED'
AND i.is_primary_key = 1
OPEN cur
FETCH NEXT FROM cur
INTO @SchemaName, @TableName, @IndexName
WHILE @@FETCH_STATUS = 0
BEGIN
    SET @SQL = 
        'ALTER TABLE [' + @SchemaName + '].[' + @TableName + '] ' +
        'DROP CONSTRAINT [' + @IndexName + ']; ' +
        'ALTER TABLE [' + @SchemaName + '].[' + @TableName + '] ' +
        'ADD CONSTRAINT [' + @IndexName + '] PRIMARY KEY CLUSTERED (RowKey ASC);'
    PRINT @SQL
    EXEC sp_executesql @SQL
    FETCH NEXT FROM cur INTO @SchemaName, @TableName, @IndexName
END
CLOSE cur
DEALLOCATE curDiese SQL Befehle erstellen bei allen Tabellen, die keinen gruppierten Index haben, einen gruppierten Index auf die Spalte “RowKey”, die in der Tabelle vorhanden sein muss.
Fazit
Wenn alle Skripte ausgeführt wurden, sollte sich die Performance z.B. von Pipeline-Ausführungen verbessern und der belegte Speicherplatz der Datenbank sollte sich verringern. Als mögliche Folgemaßnahme kann die Datenbank-Datei verkleinert werden, um zusätzlichen Speicherplatz auf der Festplatte freizugeben.
