JTL-Wawi

JTL-Wawi Update 2.1: „Dateigruppe PRIMARY ist voll“ bei SQL Express – so lief es trotzdem durch

49 GB frei auf der Platte, und trotzdem ist die Datenbank voll. Das Update auf 2.1 kopiert alle Bilder einmal komplett, und das sprengt das 10-GB-Limit von SQL Express.

Überfüllter Papierstapel in einer Ringmechanik auf einem hellen Schreibtisch am Fenster, daneben ein Laptop mit Stift
Platz auf der Platte ist genug. Das Limit sitzt in der Datenbank-Edition.

Ein Update von JTL-Wawi 2.0.4.0 auf 2.1.3.0, Datenbank auf SQL Server 2017 Express. Nach einigen Minuten bricht es in Version 2.1.0.0 ab, mit dieser Meldung:

Speicherplatz für das “dbo.tBild”.’PK_dbo_tBild’-Objekt in der eazybusiness-Datenbank konnte nicht belegt werden, da die Dateigruppe “PRIMARY” voll ist.

Ausgelöst hat es der Befehl:

ALTER TABLE [dbo].[tBild] ALTER COLUMN [bBild] varbinary(max) NOT NULL;

Auf der Festplatte waren 49 GB frei. Am Speicherplatz lag es also nicht. Die Ursache war das 10-GB-Datenlimit von SQL Server Express. Das gilt für Express 2017 genauso wie für 2019, 2022 und 2025, und die Logdatei zählt dabei nicht mit.

Hier steht, warum gerade dieses Update an die Grenze stößt, wie man das vorher misst und wie wir es ohne Editionswechsel durchbekommen haben. Auch die Irrwege stehen drin.

Was die Migration macht

Im Update-Fenster unter „Details“ steht die Migration WAWI-87515_ExtendImageStructure. Sie stellt die Bildspalten in tBild auf varbinary(max) um. Der entscheidende Befehl:

UPDATE [dbo].[tBild]
SET [bBild_new]         = CAST([bBild] AS varbinary(max)),
    [bVorschauBild_new] = CAST([bVorschauBild] AS varbinary(max))

Das heißt:

  1. Alle Bilddaten werden in neue Spalten kopiert. Die Bilder liegen jetzt doppelt in der Datenbank.
  2. Die alte Spalte wird gelöscht. Der Platz wird dabei aber nicht frei, er bleibt als Ballast in der Tabelle liegen.
  3. Beim ALTER … NOT NULL wird alles noch einmal geschrieben.

Der Spitzenbedarf liegt damit grob bei:

Belegt + 2 × Bilddaten + Verwaltungsaufwand

Die echten Zahlen

Belegt vor dem Update 4,77 GB
davon Bilddaten in tBild 3,5 GB (8.212 Bilder)
Belegung nach der Kopie ca. 8,8 GB
Datendatei beim Abbruch 10.200 MB

4,77 + 2 × 3,5 ergibt knapp 12 GB, und das passt nicht in 10 GB. Die Kopie ging noch durch, beim ALTER war Schluss.

Diagnose: wie voll ist die Datenbank wirklich?

Die folgenden Abfragen sind alle nur lesend. Sie laufen im SQL Server Management Studio auf der Wawi-Datenbank (Standard: eazybusiness, bei weiteren Mandanten z. B. eazybusiness_tm2).

Edition und Version:

SELECT SERVERPROPERTY('Edition')        AS Edition,
       SERVERPROPERTY('ProductVersion') AS Version;

Steht bei Edition Express Edition, gilt das 10-GB-Limit. Version 14.0 ist SQL Server 2017.

Dateigröße und Belegung:

USE eazybusiness;
SELECT name                                   AS Datei,
       type_desc                              AS Typ,
       size / 128.0                           AS [Dateigröße MB],
       FILEPROPERTY(name, 'SpaceUsed') / 128.0 AS [Belegt MB]
FROM sys.database_files;

Maßgeblich ist Belegt MB der Zeile mit Typ ROWS. Die Logdatei (LOG) zählt nicht zum Limit.

Die größten Tabellen:

SELECT TOP 10
       t.name                          AS Tabelle,
       SUM(au.used_pages) / 128.0      AS [Belegt MB],
       SUM(au.total_pages) / 128.0     AS [Reserviert MB]
FROM sys.tables t
JOIN sys.partitions p        ON p.object_id = t.object_id
JOIN sys.allocation_units au ON au.container_id =
     CASE au.type WHEN 2 THEN p.partition_id ELSE p.hobt_id END
GROUP BY t.name
ORDER BY [Belegt MB] DESC;

Steht tBild ganz oben, betrifft Sie dieser Beitrag.

Wie viel davon sind Bilddaten?

SELECT COUNT(*)                                                    AS Bilder,
       SUM(CAST(DATALENGTH(bBild) AS bigint)) / 1048576.0          AS [Bilddaten MB],
       SUM(CAST(DATALENGTH(bVorschauBild) AS bigint)) / 1048576.0  AS [Vorschaubilder MB],
       MAX(DATALENGTH(bBild)) / 1048576.0                          AS [Größtes Bild MB]
FROM dbo.tBild;

Damit haben Sie die Werte für die Faustregel: Belegt + 2 × (Bilddaten + Vorschaubilder) < 10 GB, und zwar mit Luft.

Die Irrwege

LOB-Komprimierung: nichts gewonnen

Der erste Gedanke war, die Tabelle zu verdichten:

ALTER INDEX ALL ON dbo.tBild REORGANIZE WITH (LOB_COMPACTION = ON);

Das brachte nichts. Die LOB-Seiten waren bereits zu 97 % gefüllt, zu verdichten gab es nichts.

DBCC CLEANTABLE: Platz frei, Problem nicht gelöst

Nach dem Abbruch lag der Ballast der gelöschten Spalte noch in der Tabelle. Den bekommt man so los:

DBCC CLEANTABLE('eazybusiness', 'dbo.tBild', 500);

Das gab 1,5 GB frei. Geholfen hat es trotzdem nicht. JTL verlangt nach einem abgebrochenen Update zwingend das Backup von vorher:

Das zuvor erstellte Backup muss wieder eingespielt werden!

„Reparieren“ hilft nicht. Und im Backup steckt der Ballast gar nicht, er entsteht bei jedem Updatelauf neu. Aufräumen nach dem Abbruch ist deshalb verlorene Mühe. Platz schaffen muss man in der zurückgespielten Datenbank, vor dem nächsten Lauf.

Das Backup zurückspielen, wenn der JTL-Dialog streikt

Der Importdialog von JTL scheiterte bei uns still. Ursache waren noch offene Verbindungen auf die Datenbank. Per SSMS geht es schneller und nachvollziehbarer.

Vorher: Wawi auf allen Arbeitsplätzen schließen, Worker-Dienst stoppen.

Schritt 1: das richtige Backup finden. Nicht blind das neueste nehmen. Gebraucht wird das Vollbackup von vor dem Update:

SELECT TOP 5
       bs.backup_finish_date   AS [Fertig am],
       bs.is_copy_only         AS [Copy-only],
       mf.physical_device_name AS Pfad
FROM msdb.dbo.backupset bs
JOIN msdb.dbo.backupmediafamily mf ON mf.media_set_id = bs.media_set_id
WHERE bs.database_name = N'eazybusiness'
  AND bs.type = 'D'
ORDER BY bs.backup_finish_date DESC;

Schritt 2: zurückspielen. Den Pfad aus Schritt 1 einsetzen:

USE master;
DECLARE @pfad nvarchar(400) = N'<Pfad aus Schritt 1>';

ALTER DATABASE eazybusiness SET OFFLINE WITH ROLLBACK IMMEDIATE;
RESTORE DATABASE eazybusiness FROM DISK = @pfad WITH REPLACE, STATS = 10;

ROLLBACK IMMEDIATE wirft alle noch offenen Verbindungen hinaus, deshalb vorher wirklich alles schließen. Der Restore hat bei uns etwa 40 Sekunden gedauert. Wer das nicht regelmäßig übt, findet im Beitrag zum Restore-Test einen Grund mehr dafür.

Programm 2.1, Datenbank 2.0

Was danach leicht verwirrt: Das Programm 2.1.3 bleibt installiert. Die zurückgespielte 2.0.4-Datenbank wird beim nächsten Start der Wawi einfach erneut aktualisiert, und dann scheitert sie wieder an derselben Stelle.

Wer zwischendurch weiterarbeiten muss, installiert auf allen Rechnern wieder 2.0.4 und bricht die Update-Meldungen ab, bis der Platz geschaffen ist.

Die Lösung: verwaiste Bilder

Die Datenbank hatte eine Vorgeschichte. Kurz vorher waren über 1.500 Artikel gelöscht worden. Beim Löschen entfernt die Wawi die Verknüpfung zum Bild (tArtikelbildPlattform). Die Bilddaten selbst bleiben in tBild liegen, ohne dass irgendetwas noch darauf zeigt.

Verwaiste Bilder finden

Welche Tabellen auf ein Bild zeigen können, ändert sich von Version zu Version. Deshalb prüft die folgende Abfrage dynamisch jede Spalte namens kBild in der Datenbank und zählt die Bilder, auf die keine davon verweist:

-- nur lesend
DECLARE @sql nvarchar(max) = N'';

SELECT @sql += N' AND NOT EXISTS (SELECT 1 FROM '
            + QUOTENAME(s.name) + N'.' + QUOTENAME(t.name)
            + N' x WHERE x.kBild = b.kBild)'
FROM sys.columns c
JOIN sys.tables  t ON t.object_id = c.object_id
JOIN sys.schemas s ON s.schema_id = t.schema_id
WHERE c.name = N'kBild'
  AND t.name <> N'tBild';

SET @sql = N'SELECT COUNT(*) AS [Verwaiste Bilder],
    SUM(CAST(DATALENGTH(b.bBild) AS bigint)
      + ISNULL(CAST(DATALENGTH(b.bVorschauBild) AS bigint), 0)) / 1048576.0 AS [MB]
FROM dbo.tBild b
WHERE 1 = 1' + @sql;

PRINT @sql;   -- prüfen, welche Tabellen einbezogen sind
EXEC sp_executesql @sql;

Ergebnis: 4.022 verwaiste Bilder mit 1,9 GB. Mehr als die Hälfte der Bildtabelle.

Ein Hinweis zu den Spaltennamen: Spalten wie kArtikelbildPlattform oder kMerkmalBildPlattform enthalten IDs der Verknüpfungstabellen, keine direkten Bildverweise. Sie gehören nicht in diese Prüfung. Die Verknüpfungstabellen selbst haben eine Spalte kBild und werden deshalb automatisch erfasst. Prüfen Sie die Liste aus dem PRINT trotzdem einmal mit dem Auge.

Löschen, abgesichert

Achtung, dieser Teil verändert Daten. Schließen Sie vorher die Wawi auf allen Arbeitsplätzen, stoppen Sie den Worker-Dienst und ziehen Sie ein eigenes Backup. Schließen Sie die Transaktion sofort ab, entweder mit COMMIT oder mit ROLLBACK. Eine offene Transaktion sperrt tBild für alle.

Backup, als COPY_ONLY, damit es die reguläre Sicherungskette nicht stört:

BACKUP DATABASE eazybusiness
TO DISK = N'D:\Backup\eazybusiness_vor_bildbereinigung.bak'
WITH COPY_ONLY, STATS = 10;

Löschen mit Vorher-/Nachher-Zählung:

USE eazybusiness;

IF OBJECT_ID('tempdb..#verwaist') IS NOT NULL DROP TABLE #verwaist;
CREATE TABLE #verwaist (kBild int PRIMARY KEY);

DECLARE @sql nvarchar(max) = N'';
SELECT @sql += N' AND NOT EXISTS (SELECT 1 FROM '
            + QUOTENAME(s.name) + N'.' + QUOTENAME(t.name)
            + N' x WHERE x.kBild = b.kBild)'
FROM sys.columns c
JOIN sys.tables  t ON t.object_id = c.object_id
JOIN sys.schemas s ON s.schema_id = t.schema_id
WHERE c.name = N'kBild' AND t.name <> N'tBild';

SET @sql = N'INSERT INTO #verwaist (kBild) SELECT b.kBild FROM dbo.tBild b WHERE 1 = 1' + @sql;
EXEC sp_executesql @sql;

BEGIN TRANSACTION;

SELECT COUNT(*) AS [Vorher] FROM dbo.tBild;

DELETE b
FROM dbo.tBild b
JOIN #verwaist v ON v.kBild = b.kBild;
SELECT @@ROWCOUNT AS [Gelöscht];

SELECT COUNT(*) AS [Nachher] FROM dbo.tBild;

-- Stimmt Vorher − Gelöscht = Nachher und entspricht "Gelöscht" der Zählung oben?
-- Dann: COMMIT TRANSACTION;   Sonst: ROLLBACK TRANSACTION;

Bei uns: 8.212 / 4.022 / 4.190. Die Zahlen gingen auf, also COMMIT.

Der Schritt, ohne den nichts frei wird

Nach dem Löschen ist der Platz noch nicht frei. Die gelöschten LOB-Daten bleiben belegt, bis sie verdichtet werden:

CHECKPOINT;
ALTER INDEX ALL ON dbo.tBild REORGANIZE WITH (LOB_COMPACTION = ON);

Hier wirkt die LOB-Komprimierung, die vorher nichts gebracht hatte: tBild schrumpfte von 4.644 MB auf 2.717 MB. Ohne diesen Schritt hätte das Update dieselbe Belegung gesehen wie vorher.

Update erneut, ohne Performance-Optimierung

Beim erneuten Start haben wir im Update-Dialog die Option „Performance-Optimierung“ abgewählt. Sie macht zusätzliche Indexarbeit, die man später in Ruhe nachholen kann, und braucht ebenfalls Platz.

Ergebnis: Die Kopie der Bilddaten hob die Belegung auf 4,8 GB, beim ALTER blieben 5,4 GB Luft. Das Update lief durch.

Was Sie vor dem Update auf 2.1 tun sollten

  1. Belegung messen. Faustregel: Belegt + 2 × Bilddaten < 10 GB, mit Reserve. Die Abfragen oben dauern zwei Minuten.
  2. Verwaiste Bilder aufräumen, besonders wenn in den letzten Jahren größere Mengen Artikel gelöscht wurden.
  3. Große Bilder verkleinern. Für die Wawi reicht ein JPG mit etwa 1.500 px an der langen Kante. Ein 8-MB-Original aus der Kamera braucht in der Datenbank niemand.
  4. Das Update planen wie ein Projekt, mit Rückfallgrenze und getesteter Sicherung. Die Reihenfolge von Wawi, Clients, Worker, Connector und Shop gilt hier genauso.

Langfristig: raus aus Express

Aufräumen verschafft Luft, löst aber nicht das Grundproblem. Eine Wawi, die wächst, stößt mit Express früher oder später wieder an.

Meine Einschätzung:

  • SQL Server Standard in der Lizenzierung Server + CAL ist für kleine Betriebe mit wenigen Arbeitsplätzen meist die günstigere Variante gegenüber der Core-Lizenz.
  • Nicht SQL Server 2017 nehmen. Der erweiterte Support endet im Oktober 2027. Wer jetzt investiert, nimmt 2022.
  • Der Wechsel ist kein Neuaufbau. Edition- und Versionsupgrade laufen über das SQL-Server-Setup, die Datenbanken bleiben erhalten. Vorher sichern versteht sich trotzdem.
  • Für Hosting-Anbieter gilt eine andere Rechnung: Wer SQL Server für Kunden bereitstellt, lizenziert über SPLA und nicht über Retail-Lizenzen.

Das Update auf 2.1 ist nicht das letzte, das die Datenbank umbaut. Wer bei 7 von 10 GB steht, sollte die Edition jetzt in Ruhe wechseln und nicht erst beim nächsten Abbruch.

Häufige Fragen

Was bedeutet „Dateigruppe PRIMARY ist voll“ beim JTL-Wawi-Update?

Die Datenbank hat ihre maximale Größe erreicht. Bei SQL Server Express liegt die Grenze bei 10 GB Daten pro Datenbank, unabhängig davon, wie viel Platz auf der Festplatte frei ist. Die Logdatei zählt nicht mit.

Warum braucht das Update auf JTL-Wawi 2.1 so viel Platz?

Die Migration WAWI-87515_ExtendImageStructure stellt die Bildspalten in tBild auf varbinary(max) um. Dazu kopiert sie alle Bilddaten in neue Spalten und schreibt sie beim Umstellen auf NOT NULL noch einmal. Grob gerechnet braucht es kurzzeitig: belegter Platz plus zweimal die Bilddaten.

Kann ich nach dem Abbruch einfach auf „Reparieren“ klicken?

Nein. JTL verlangt nach einem abgebrochenen Update, dass das Backup von vor dem Update eingespielt wird. Erst danach darf das Update erneut gestartet werden, und vorher muss der Platzbedarf sinken.

Wie prüfe ich vor dem Update, ob es mit SQL Express reicht?

Faustregel: belegter Platz der Datenbank plus zweimal die Bilddaten in tBild muss deutlich unter 10 GB bleiben. Die SQL-Abfragen dafür stehen im Beitrag. Liegt der Wert darüber, erst verwaiste Bilder aufräumen oder auf eine größere Edition wechseln.

Löscht JTL-Wawi die Bilder, wenn ich Artikel lösche?

Nicht zuverlässig. In unserem Fall entfernte die Wawi beim Löschen von über 1.500 Artikeln nur die Verknüpfungen. Die Bilddaten blieben in tBild liegen, 4.022 Bilder mit zusammen 1,9 GB.

Klingt nach Ihrem Thema?

Kurzer Anruf, ehrliche Einschätzung, kein Vertriebsgerede. Ich sage Ihnen auch, wenn ich der Falsche bin.

JTL Service Partner Silber: offizielles Partnerabzeichen von JTL Offizieller JTL-Servicepartner Stufe Silber · 5,0 ★ aus 5 Bewertungen bei JTL Profil bei JTL ansehen →