Symfonia FK 2026.3: „Błąd zapisu do bufora" — poprawka na wielu bazach naraz
Symfonia FK 2026.3 przy dokumentach w oknie Faktury KSeF potrafi rzucić „Błąd zapisu do bufora. Nieprawidłowy format ciągu wejściowego". Producent ma na to poprawkę SQL — pokazuję, jak wykonać ją na wielu bazach naraz, bezpiecznie, z testowym przebiegiem przez ROLLBACK.
Komu to pomoże?
Tobie, jeśli masz na jednym SQL Serverze kilkadziesiąt albo kilkaset baz Symfonii FK 2026.3 i przy wprowadzaniu lub aktualizacji dokumentów w oknie Faktury KSeF widzisz:
Błąd zapisu do bufora. Nieprawidłowy format ciągu wejściowego
Poprawkę producenta trzeba wtedy wykonać na każdej bazie z osobna — a przy takiej liczbie klientów to zwykłe marnowanie popołudnia.
Kogo to nie dotyczy?
Masz jedną bazę? Nie kombinuj — wykonaj po prostu oficjalny skrypt Symfonii na tej jednej bazie i tyle. Ten wpis jest o robieniu tego samego hurtem, na wielu bazach.
Na czym polega problem
Błąd pojawia się w Symfonii Finanse i Księgowość 2026.3 podczas wprowadzania do bufora lub aktualizacji dokumentów w oknie Faktury KSeF. Symfonia opublikowała do tego oficjalną poprawkę SQL i zaleca ją wyłącznie dla wersji 2026.3.
Oficjalna instrukcja producenta zakłada wykonanie skryptu na konkretnej bazie firmy. Przy jednej bazie to żaden problem. Gorzej, kiedy na jednym SQL Serverze pracuje kilkadziesiąt albo kilkaset baz klientów — ręczne przechodzenie po każdej z osobna jest po prostu niewygodne.
Co robi mój skrypt
Przygotowałem wersję, która przechodzi po bazach SQL automatycznie. Skrypt:
- sprawdza wszystkie dostępne bazy użytkownika (pomija systemowe i niedostępne),
- w każdej bazie szuka w
FK.dokumentyrekordów wymagających poprawki, - przed zmianą tworzy kopię tylko tych rekordów, które mają zostać zmodyfikowane,
- dopiero wtedy wykonuje zmianę,
- pilnuje, żeby liczba rekordów w kopii i liczba zmienionych zgadzały się co do sztuki,
- na końcu pokazuje raport z każdej bazy.
Nie ma więc potrzeby ręcznego otwierania każdej bazy.
Trzy ustawienia na górze skryptu
Zmieniasz tylko sekcję USTAWIENIA:
DECLARE @NowyZnacznik nvarchar(100) = N'0';
DECLARE @Tryb varchar(8) = 'ROLLBACK';
DECLARE @TabelaKopii sysname = N'dokumenty_backup_20260806';
@NowyZnacznik— wartość, którą skrypt wpisuje w brakujący znacznik (zgodnie z poprawką producenta).@Tryb—ROLLBACK(test) alboCOMMIT(zapis). O tym niżej.@TabelaKopii— nazwa tabeli kopii, którą skrypt tworzy w schemacieFK(czyli powstanieFK.dokumenty_backup_20260806).
Tryb testowy (ROLLBACK) i zapis (COMMIT)
To jest bezpiecznik. Przy:
DECLARE @Tryb varchar(8) = 'ROLLBACK';
skrypt działa testowo: wchodzi w transakcję, liczy rekordy, próbnie tworzy kopię, wykonuje aktualizację — a na końcu wszystko wycofuje. Nic nie zostaje zapisane, ale w raporcie widzisz, ile rekordów w której bazie by się zmieniło. To Twój przebieg na sucho.
Kiedy wynik wygląda poprawnie, zmieniasz jedną rzecz:
DECLARE @Tryb varchar(8) = 'COMMIT';
i uruchamiasz skrypt ponownie. Teraz kopia zostaje utworzona, a zmiany zapisane.
Kopia zmienianych rekordów
Przy COMMIT skrypt zapisuje do tabeli FK.<@TabelaKopii> rekordy sprzed zmiany —
dokładnie te, które modyfikuje, nic więcej. Dzięki temu, obok standardowej kopii
zapasowej SQL Server, masz szybką kopię właśnie tych danych.
Dwa zabezpieczenia, o których warto wiedzieć:
- Skrypt nie nadpisze istniejącej kopii — jeśli tabela o tej nazwie już jest
w danej bazie, baza zostaje pominięta (żeby nie stracić wcześniejszej kopii).
Przy powtórnym uruchomieniu zmień
@TabelaKopii(np. na inną datę). - Jeśli liczba rekordów w kopii nie zgadza się z liczbą do zmiany albo z liczbą faktycznie zmienionych — skrypt przerywa i wycofuje zmianę w tej bazie.
To nie zastępuje pełnego backupu bazy, ale bardzo ułatwia późniejszą weryfikację.
Zanim uruchomisz
Symfonia w swojej instrukcji również zaleca wykonanie kopii bezpieczeństwa bazy oraz zamknięcie aplikacji na wszystkich stanowiskach przed wykonaniem poprawki. Ja robię to samo. Przed uruchomieniem:
- wykonaj backup baz,
- zamknij Symfonię na stanowiskach,
- najpierw uruchom skrypt z
@Tryb = 'ROLLBACK', - sprawdź wynik,
- dopiero później zmień na
@Tryb = 'COMMIT'.
Jak uruchomić w SQL Server Management Studio
- Uruchom SSMS i połącz się z serwerem, na którym stoją bazy Symfonii.
- Kliknij New Query / Nowe zapytanie.
- Wklej skrypt (poniżej).
- Ustaw sekcję
USTAWIENIA— zostaw@Tryb = 'ROLLBACK'na pierwszy przebieg. - Naciśnij F5 albo kliknij Execute / Wykonaj.
- Sprawdź raport na dole okna.
- Jeżeli wszystko wygląda prawidłowo, ustaw
@Tryb = 'COMMIT'i uruchom ponownie.
Skrypt dla wielu baz
⬇ Pobierz gotowy skrypt (.sql)
USE [master];
GO
SET NOCOUNT ON;
SET XACT_ABORT ON;
/* ============================================================
USTAWIENIA — ZMIENIASZ TYLKO TUTAJ
============================================================ */
DECLARE @NowyZnacznik nvarchar(100) = N'0';
-- ROLLBACK = test, nic nie zostanie zapisane
-- COMMIT = utworzenie kopii i zapisanie zmian
DECLARE @Tryb varchar(8) = 'ROLLBACK';
-- Nazwa tabeli kopii tworzonej w schemacie FK
DECLARE @TabelaKopii sysname = N'dokumenty_backup_20260806';
/* ============================================================
WALIDACJA TRYBU
============================================================ */
SET @Tryb = UPPER(LTRIM(RTRIM(@Tryb)));
IF @Tryb NOT IN ('COMMIT', 'ROLLBACK')
BEGIN
THROW 50000,
N'Nieprawidłowy tryb. Ustaw @Tryb na COMMIT albo ROLLBACK.',
1;
END;
DECLARE @CzyCommit bit =
CASE
WHEN @Tryb = 'COMMIT' THEN 1
ELSE 0
END;
/* ============================================================
TABELA WYNIKOWA
============================================================ */
DROP TABLE IF EXISTS #FK_Wyniki;
CREATE TABLE #FK_Wyniki
(
BazaDanych sysname NULL,
RekordyDoZmiany bigint NOT NULL,
RekordySkopiowane bigint NOT NULL,
RekordyZmienione bigint NOT NULL,
-- 0 = brak zmian
-- 1 = wykonano poprawnie
-- 2 = pominięto
-- 3 = błąd
StatusKod tinyint NOT NULL,
StatusOpis nvarchar(1000) NULL
);
/* ============================================================
ZMIENNE KURSORA
============================================================ */
DECLARE @Baza sysname;
DECLARE @sql nvarchar(max);
DECLARE @TabelaKopiiQuoted nvarchar(258);
SET @TabelaKopiiQuoted = QUOTENAME(@TabelaKopii);
/* ============================================================
LISTA BAZ
============================================================ */
DECLARE kursor_baz CURSOR LOCAL FAST_FORWARD FOR
SELECT [name]
FROM sys.databases
WHERE database_id > 4
AND state_desc = N'ONLINE'
AND is_read_only = 0
AND source_database_id IS NULL
AND HAS_DBACCESS([name]) = 1
ORDER BY [name];
OPEN kursor_baz;
FETCH NEXT FROM kursor_baz INTO @Baza;
/* ============================================================
PRZEJŚCIE PO BAZACH
============================================================ */
WHILE @@FETCH_STATUS = 0
BEGIN
SET @sql = N'
USE ' + QUOTENAME(@Baza) + N';
DECLARE @DoZmiany bigint = 0;
DECLARE @Skopiowano bigint = 0;
DECLARE @Zmieniono bigint = 0;
BEGIN TRY
/* Sprawdzenie tabeli oraz wymaganych kolumn */
IF OBJECT_ID(N''[FK].[dokumenty]'', N''U'') IS NULL
OR COL_LENGTH(N''[FK].[dokumenty]'', N''zrodlo'') IS NULL
OR COL_LENGTH(N''[FK].[dokumenty]'', N''znacznik'') IS NULL
BEGIN
INSERT INTO #FK_Wyniki
(
BazaDanych,
RekordyDoZmiany,
RekordySkopiowane,
RekordyZmienione,
StatusKod,
StatusOpis
)
VALUES
(
DB_NAME(),
0,
0,
0,
2,
N''Pominięto: brak tabeli FK.dokumenty lub wymaganych kolumn''
);
END
ELSE
BEGIN
/* Policzenie rekordów do zmiany */
SELECT @DoZmiany = COUNT_BIG(*)
FROM [FK].[dokumenty]
WHERE [zrodlo] = 2
AND
(
[znacznik] IS NULL
OR LTRIM(RTRIM(CONVERT(nvarchar(4000), [znacznik]))) = N''''
);
IF @DoZmiany = 0
BEGIN
INSERT INTO #FK_Wyniki
(
BazaDanych,
RekordyDoZmiany,
RekordySkopiowane,
RekordyZmienione,
StatusKod,
StatusOpis
)
VALUES
(
DB_NAME(),
0,
0,
0,
0,
N''Brak rekordów do zmiany''
);
END
ELSE
BEGIN
/*
Jeśli tabela kopii już istnieje, nie wykonujemy UPDATE.
Zapobiega to nadpisaniu wcześniejszej kopii.
*/
IF OBJECT_ID(
N''[FK].' + REPLACE(@TabelaKopiiQuoted, '''', '''''') + N''',
N''U''
) IS NOT NULL
BEGIN
INSERT INTO #FK_Wyniki
(
BazaDanych,
RekordyDoZmiany,
RekordySkopiowane,
RekordyZmienione,
StatusKod,
StatusOpis
)
VALUES
(
DB_NAME(),
@DoZmiany,
0,
0,
2,
N''Pominięto: tabela kopii już istnieje''
);
END
ELSE
BEGIN
BEGIN TRANSACTION;
/* Kopia wyłącznie rekordów, które będą zmieniane */
SELECT *
INTO [FK].' + @TabelaKopiiQuoted + N'
FROM [FK].[dokumenty]
WHERE [zrodlo] = 2
AND
(
[znacznik] IS NULL
OR LTRIM(RTRIM(CONVERT(nvarchar(4000), [znacznik]))) = N''''
);
/* Policzenie rekordów w kopii */
SELECT @Skopiowano = COUNT_BIG(*)
FROM [FK].' + @TabelaKopiiQuoted + N';
IF @Skopiowano <> @DoZmiany
BEGIN
THROW 50001,
N''Liczba rekordów w kopii nie zgadza się z liczbą rekordów do zmiany.'',
1;
END;
/* Aktualizacja */
UPDATE [FK].[dokumenty]
SET [znacznik] = @pNowyZnacznik
WHERE [zrodlo] = 2
AND
(
[znacznik] IS NULL
OR LTRIM(RTRIM(CONVERT(nvarchar(4000), [znacznik]))) = N''''
);
SET @Zmieniono = @@ROWCOUNT;
IF @Zmieniono <> @Skopiowano
BEGIN
THROW 50002,
N''Liczba zmienionych rekordów nie zgadza się z liczbą rekordów w kopii.'',
1;
END;
/* @pCzyCommit jest typu bit — brak konfliktu collation */
IF @pCzyCommit = 1
BEGIN
COMMIT TRANSACTION;
INSERT INTO #FK_Wyniki
(
BazaDanych,
RekordyDoZmiany,
RekordySkopiowane,
RekordyZmienione,
StatusKod,
StatusOpis
)
VALUES
(
DB_NAME(),
@DoZmiany,
@Skopiowano,
@Zmieniono,
1,
N''OK: kopia utworzona, zmiany zapisane''
);
END
ELSE
BEGIN
ROLLBACK TRANSACTION;
INSERT INTO #FK_Wyniki
(
BazaDanych,
RekordyDoZmiany,
RekordySkopiowane,
RekordyZmienione,
StatusKod,
StatusOpis
)
VALUES
(
DB_NAME(),
@DoZmiany,
@Skopiowano,
@Zmieniono,
1,
N''TEST OK: zmiana i tabela kopii zostały wycofane''
);
END;
END;
END;
END;
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0
ROLLBACK TRANSACTION;
INSERT INTO #FK_Wyniki
(
BazaDanych,
RekordyDoZmiany,
RekordySkopiowane,
RekordyZmienione,
StatusKod,
StatusOpis
)
VALUES
(
DB_NAME(),
ISNULL(@DoZmiany, 0),
ISNULL(@Skopiowano, 0),
ISNULL(@Zmieniono, 0),
3,
N''BŁĄD: '' + ERROR_MESSAGE()
);
END CATCH;
';
EXEC sys.sp_executesql
@sql,
N'
@pNowyZnacznik nvarchar(100),
@pCzyCommit bit
',
@pNowyZnacznik = @NowyZnacznik,
@pCzyCommit = @CzyCommit;
FETCH NEXT FROM kursor_baz INTO @Baza;
END;
CLOSE kursor_baz;
DEALLOCATE kursor_baz;
/* ============================================================
WYNIKI — BAZY Z REKORDAMI LUB BŁĘDAMI
============================================================ */
SELECT
BazaDanych,
RekordyDoZmiany,
RekordySkopiowane,
RekordyZmienione,
CASE StatusKod
WHEN 0 THEN N'Brak zmian'
WHEN 1 THEN N'OK'
WHEN 2 THEN N'Pominięto'
WHEN 3 THEN N'Błąd'
END AS Status,
StatusOpis
FROM #FK_Wyniki
WHERE RekordyDoZmiany > 0
OR StatusKod = 3
ORDER BY
RekordyDoZmiany DESC,
BazaDanych;
/* ============================================================
PODSUMOWANIE
============================================================ */
SELECT
@NowyZnacznik AS NowaWartoscZnacznika,
@Tryb AS TrybWykonania,
SUM(RekordyDoZmiany) AS RazemRekordowDoZmiany,
SUM(RekordySkopiowane) AS RazemSkopiowano,
SUM(RekordyZmienione) AS RazemZmieniono,
SUM(CASE WHEN StatusKod = 1 THEN 1 ELSE 0 END)
AS LiczbaPrzetworzonychBaz,
SUM(CASE WHEN StatusKod = 2 THEN 1 ELSE 0 END)
AS LiczbaPominietychBaz,
SUM(CASE WHEN StatusKod = 3 THEN 1 ELSE 0 END)
AS LiczbaBledow
FROM #FK_Wyniki;
/* ============================================================
BŁĘDY I POMINIĘTE BAZY
============================================================ */
SELECT
BazaDanych,
StatusOpis
FROM #FK_Wyniki
WHERE StatusKod IN (2, 3)
ORDER BY BazaDanych;
Oficjalna poprawka Symfonii
Opis problemu i oryginalna poprawka producenta: F50/FKF 2026.3 — Błąd zapisu do bufora. Nieprawidłowy format ciągu wejściowego.
Na koniec
Jeżeli masz jedną bazę, skrypt producenta w zupełności wystarczy. Ale jeśli na jednej
instancji SQL trzymasz kilkadziesiąt lub kilkaset baz Symfonii, wygodniej zrobić to
raz, centralnie — z raportem i z możliwością wcześniejszego testu przez ROLLBACK.
Coś u Ciebie zjada czas co miesiąc, a wygląda na „program tego nie robi"? Napisz do mnie — zwykle da się dorobić przycisk.
Masz pytanie do tego wpisu?
Napisz w dwóch zdaniach, o co chodzi. Nie musisz znać technicznej nazwy problemu — dopytam o resztę. Pierwsze rozeznanie jest bezpłatne.
- kontakt [at] panodit [dot] pl
- +48 737 458 ***879
- Większa sprawa? Pełny formularz →