Asystent do raportów SQL w codziennej pracy działu

Asystent do raportów SQL przyjmuje pytanie w języku polskim, buduje z niego zapytanie do bazy i pokazuje wynik jako tabelę. Według opisu SoftwareStudio użytkownik może tę tabelę sortować i filtrować, a także grupować według dowolnej kolumny, po czym wyeksportować ją do Excela. Sam mechanizm generowania zapytań opisuje artykuł o asystencie DataFlow AI, a ten tekst dotyczy pracy z wynikiem.

Zmiana dla organizacji polega na tym, że raporty mogą przygotowywać nie tylko specjaliści IT, lecz także menedżerowie i analitycy, a także pracownicy operacyjni. Dział informatyki nie znika z procesu. Przygotowuje widoki i nadaje uprawnienia, a za definicje odpowiada wspólnie z właścicielami danych. Pojedyncze pytanie zadaje już ten, kto potrzebuje odpowiedzi.

Kto o co pyta

Tabela zestawia przykładowe role i pytania. Każde z nich ma jednoznaczny wynik zapisany w bazie, co odróżnia je od pytań o opinię lub prognozę.

RolaPrzykładowe pytanieObszar danych
Kierownik sprzedażyKtórzy klienci zmniejszyli zamówienia w tym kwartaleSprzedaż w systemie ERP
Kierownik magazynuKtóre pozycje mają zapas poniżej minimumStany magazynowe
Koordynator ISOKtóre dokumenty mają przeterminowany przeglądRejestr dokumentów procedur
HandlowiecKiedy dany klient ostatnio złożył zamówienieHistoria kontaktów w CRM
Przykładowe pytania zadawane asystentowi i dane, z których powstaje odpowiedź.

Asystent raportowy najlepiej sprawdza się tam, gdzie odpowiedź jest zapisana w bazie i wymaga tylko wybrania wierszy i ich zsumowania. Pytania o to, dlaczego sprzedaż spadła, wykraczają poza jego rolę: tabela pokazuje, co się stało, a nie dlaczego.

Tabela wyników - praca z wierszami w przeglądarce i w bazie

Interaktywna tabela wygląda prosto, ale każda jej operacja może wykonywać się w dwóch miejscach: w przeglądarce, na wierszach już pobranych, albo w bazie, przez ponowne zapytanie. Od tego wyboru zależą szybkość odpowiedzi i poprawność sum.

Gdzie wykonuje się operacja

Przy kilkuset wierszach sortowanie i filtrowanie w przeglądarce jest natychmiastowe. Przy większych zbiorach przeglądarka nie powinna pobierać setek tysięcy wierszy, żeby pokazać pięćdziesiąt, więc operację zleca się bazie.

OperacjaW przeglądarceW bazie danych
SortowanieSort na pobranych wierszachORDER BY, wsparte indeksem
FiltrowanieFilter na tablicy wynikówWHERE z parametrami
GrupowanieAgregacja w pamięci przeglądarkiGROUP BY po stronie serwera
StronicowanieWycinek tablicyOFFSET i FETCH
Dwa miejsca wykonania tych samych operacji na tabeli wyników.

Stronicowanie po stronie bazy wymaga deterministycznego sortowania. Gdy kolumna sortowania zawiera powtórzenia, dodaje się drugą kolumnę o unikalnych wartościach, inaczej ten sam wiersz może trafić na dwie strony albo na żadną. Składnię opisuje dokumentacja klauzuli ORDER BY z OFFSET i FETCH.

SELECT KlientId, Nazwa, SprzedazNetto
FROM rap.SprzedazKlienta
WHERE SprzedazNetto >= @Min
ORDER BY SprzedazNetto DESC, KlientId
OFFSET @Strona * @Rozmiar ROWS FETCH NEXT @Rozmiar ROWS ONLY;

Filtry jako parametry zapytania

Filtr wybrany w tabeli trafia do bazy jako parametr, a nie jako fragment tekstu zapytania. Doklejanie wartości do tekstu SQL otwiera drogę do wstrzyknięcia kodu i uniemożliwia ponowne użycie planu wykonania. Procedura sp_executesql przyjmuje tekst zapytania oraz osobno listę parametrów.

DECLARE @sql nvarchar(max) = N'
SELECT KlientId, Nazwa, SprzedazNetto
FROM rap.SprzedazKlienta
WHERE Region = @Region AND SprzedazNetto >= @Min
ORDER BY SprzedazNetto DESC;';

EXEC sys.sp_executesql @sql,
     N'@Region nvarchar(40), @Min decimal(18,2)',
     @Region = N'północ', @Min = 10000;

Nazw kolumn, po których użytkownik sortuje, nie da się przekazać parametrem. Wybiera się je z listy dozwolonych nazw i dopiero wtedy wstawia do tekstu zapytania. Zasady ochrony opisuje dokumentacja o wstrzykiwaniu kodu SQL.

Grupowanie w przeglądarce

Grupowanie po kolumnie wybranej przez użytkownika można wykonać w JavaScript, bez ponownego zapytania. Poniższa funkcja zlicza wiersze i sumuje kwotę w każdej grupie.

function grupuj(wiersze, kolumna) {
  const grupy = new Map();
  for (const w of wiersze) {
    const klucz = w[kolumna];
    const g = grupy.get(klucz) || { klucz, liczba: 0, suma: 0 };
    g.liczba += 1;
    g.suma += w.SprzedazNetto;
    grupy.set(klucz, g);
  }
  return [...grupy.values()];
}

Reguła: sumy i grupy liczone w przeglądarce obejmują tylko pobrane wiersze. Gdy wynik obcięto limitem, suma nie jest sumą całości i tabela musi to pokazać.

Eksport do arkusza kalkulacyjnego i dalsza praca z danymi

Eksport jest momentem, w którym wynik opuszcza kontrolowane środowisko. Dane trafiają do arkusza, gdzie można je dowolnie zmieniać i przesyłać dalej. Od sposobu zapisu zależy, czy dane dotrą do arkusza w niezmienionej postaci.

Format CSV czy XLSX

Prostszy w wykonaniu jest plik CSV, ale niesie kilka pułapek związanych z ustawieniami regionalnymi.

FormatZaletaPułapka
CSVProsty zapis tekstowy, mały rozmiarPolskie ustawienia Excela oczekują średnika jako separatora, a znaki diakrytyczne wymagają kodowania UTF-8 ze znacznikiem BOM
XLSXZachowuje typy kolumn i szerokościWymaga biblioteki do zapisu skoroszytu po stronie serwera
Dwa formaty eksportu i ich ograniczenia.

Wartości przekształcane przez arkusz

Excel interpretuje zawartość komórek zależnie od jej wyglądu. Kod z zerami na początku traci je, a bardzo długi numer zostaje obcięty do piętnastu cyfr znaczących. Dotyczy to na przykład osiemnastocyfrowego numeru SSCC, którego końcówka zmieni się w zera.

Siatka liczb i świetlistych pól widziana w perspektywie, ilustracja arkusza z wynikami wyeksportowanymi z bazy
Arkusz przejmuje wynik zapytania i zaczyna go interpretować po swojemu.

Kolumny z kodami oraz numerami dokumentów eksportuje się jako tekst. Osobnym ryzykiem jest tekst zaczynający się od znaku =, +, - lub @, bo arkusz może potraktować go jako formułę. Poniższa funkcja w C# neutralizuje taką wartość i cytuje pole.

static string PoleCsv(object wartosc)
{
    if (wartosc is null) return "";
    var pl = System.Globalization.CultureInfo.GetCultureInfo("pl-PL");
    if (wartosc is IFormattable f) return f.ToString(null, pl);

    var t = wartosc.ToString();
    if (t.Length > 0 && "=+-@".IndexOf(t[0]) >= 0) t = "'" + t;
    return "\"" + t.Replace("\"", "\"\"") + "\"";
}

Reguła: wartość tekstowa w eksporcie nie może zaczynać się od znaku, który arkusz odczyta jako początek formuły, a kolumny identyfikatorów zapisuje się jako tekst.

Weryfikacja wyniku przed decyzją

Tabela z wynikiem wygląda równie wiarygodnie, gdy jest poprawna i gdy zawiera błąd. Różnicę widać dopiero po porównaniu z czymś znanym. Cztery kontrole wychwytują większość pomyłek.

  • Zakres dat - odczytanie warunku z zapytania i sprawdzenie, czy obejmuje pierwszy i ostatni dzień okresu.
  • Suma kontrolna - porównanie sumy całkowitej z raportem albo zapytaniem, którego wynik jest znany.
  • Liczba wierszy - sprawdzenie, czy wynik nie został obcięty limitem.
  • Definicja miary - potwierdzenie, czy kolumna oznacza wartość netto, czy brutto.

Typowe błędy w wynikach

Najczęstsza pomyłka wynika z zapytania, które składnia SQL uznaje za poprawne, a które liczy coś innego niż zamierzono.

BłądObjawKontrola
Zdublowanie wierszy przez złączenieSuma większa niż w raporciePorównanie sumy całkowitej ze źródłem
Zły zakres datBrak ostatniego dnia okresuWarunek „mniejsze niż następny dzień”
Inna definicja miaryWartość brutto zamiast nettoOpis kolumny obok wyniku
Obcięcie wynikuSuma niższa od oczekiwanejInformacja o limicie wierszy
Cztery błędy, których nie wychwyci sprawdzenie samej składni zapytania.

Zdublowanie przez złączenie zasługuje na przykład. Klient z trzema adresami dostawy występuje w złączeniu trzy razy, więc jego sprzedaż zostaje zsumowana trzykrotnie.

-- błędne: złączenie z adresami mnoży dokumenty klienta
SELECT k.Nazwa, SUM(d.WartoscNetto) AS Suma
FROM rap.Klient AS k
JOIN rap.Dokument AS d ON d.KlientId = k.KlientId
JOIN rap.AdresDostawy AS a ON a.KlientId = k.KlientId
GROUP BY k.Nazwa;

-- kontrola: suma całkowita bez złączenia z adresami
SELECT SUM(WartoscNetto) AS SumaKontrolna
FROM rap.Dokument
WHERE DataSprzedazy >= @Od AND DataSprzedazy < @Do;

Jeżeli suma z pierwszego zapytania po zsumowaniu wszystkich klientów nie zgadza się z kontrolną, w zapytaniu jest złączenie, które mnoży wiersze.

Asystent do raportów SQL a raportowanie tradycyjne

Tradycyjny cykl przygotowania raportu obejmuje zgłoszenie i oczekiwanie w kolejce, a następnie projekt zapytania z testem. Asystent skraca etap pisania zapytania, ale nie skraca weryfikacji, dlatego zysk jest największy przy pytaniach, na które odpowiedź trzeba uzyskać raz.

Etap raportowaniaSposób tradycyjnyZ asystentem AI
Przygotowanie zapytaniaRęczne pisanie SQL przez dział ITPytanie w języku naturalnym
Czas do pierwszego wynikuZależny od kolejki zgłoszeń, od godzin do dniKilka sekund na wygenerowanie zapytania, plus czas jego wykonania
Dostępność dla użytkownikaWymaga znajomości SQL lub zgłoszeniaDostępna dla pracownika z uprawnieniem
Weryfikacja definicjiWykonuje autor raportuWykonuje użytkownik, na podstawie zapytania i opisów kolumn
Rozłożenie pracy w raportowaniu tradycyjnym i z asystentem.

Raporty cykliczne, z układem do wydruku i subskrypcją, nadal projektuje się w narzędziach do tego przeznaczonych. Podstawy opisuje artykuł o SQL Report Builder oraz dokumentacja Report Buildera w SQL Server, a działanie serwera raportów tekst o Microsoft SQL Reporting Services. Kryteria wyboru między samym zapytaniem a serwerem raportów zebrano w artykule o raportowaniu w SQL.

Podział pracy z działem IT

Praca, której nie da się powierzyć modelowi, pozostaje po stronie informatyków. Zmienia się jej charakter: mniej pojedynczych raportów, więcej utrzymania wspólnej warstwy.

  • Widoki raportowe - utrzymanie schematu z czytelnymi nazwami i opisami kolumn.
  • Uprawnienia - przydział ról oraz okresowy przegląd dostępu.
  • Definicje wskaźników - zatwierdzanie miar używanych w raportach stałych.
  • Monitoring - analiza dziennika zapytań i indeksów pod najczęstsze pytania.

Zastosowania asystenta w systemach ERP i w magazynie

Trzy obszary wymieniane przez producenta różnią się rodzajem danych i ryzykiem błędu. Poniżej opisano, jak pytanie zamienia się w zapytanie w każdym z nich.

Stany magazynowe

Pytanie o pozycje poniżej minimum dotyczy dwóch wielkości: bieżącego stanu i progu zapisanego w kartotece. Zapytanie zestawia je i sortuje według głębokości braku. Sam rejestr, z którego pochodzą liczby, opisuje artykuł o stanach magazynowych.

SELECT TOP (50) t.Symbol, t.Nazwa, s.Ilosc, t.StanMinimalny
FROM rap.StanMagazynowy AS s
JOIN rap.Towar AS t ON t.TowarId = s.TowarId
WHERE s.Ilosc < t.StanMinimalny
ORDER BY t.StanMinimalny - s.Ilosc DESC;
Kobieta w profilu patrząca na duży ekran z wykresami i tabelami, ilustracja analizy danych sprzedażowych i magazynowych
Analiza wyniku zapytania przez osobę, która zadała pytanie, bez udziału programisty.

Sprzedaż i historia kontaktów

Asystent raportowy odpowiada na pytania liczbowe: ile, kiedy, w jakiej wartości. Podpowiedzi dotyczące tego, z którym klientem rozmawiać i co powiedzieć, należą do innego narzędzia, opisanego w artykule o CRM z asystentem AI. Oba narzędzia mogą czytać te same dane, ale robią z nimi co innego.

Rejestry i procedury ISO

Rejestr dokumentów z datami przeglądów jest zwykłą tabelą, więc asystent SQL potrafi odpowiedzieć, które pozycje wymagają odnowienia. Obieg i zatwierdzanie samych dokumentów to zadanie asystenta biura, którego działanie opisuje tekst o procedurach ISO. Kontekst całej rodziny narzędzi AI daje strona o sztucznej inteligencji, a zastosowanie w logistyce magazynowej przedstawia serwis o asystencie AI w magazynie.

Uprawnienia i wdrożenie asystenta w organizacji

Im więcej osób może zadać pytanie, tym ważniejsze jest, kto co widzi. Zasięg danych wyznaczają uprawnienia bazy, a nie treść pytania. Kierownik sprzedaży dostaje dostęp do widoków sprzedażowych, a widoki płacowe pozostają poza jego rolą. Szczegóły techniczne zabezpieczeń, takich jak rola z prawem odczytu i odmowa zapisu, opisano w artykule o DataFlow AI.

Odtwarzanie liczb z przeszłości

To samo zapytanie zwraca inny wynik, gdy zmienią się dane. Spór o liczbę pokazaną w poprzednim miesiącu wymaga więc dwóch rzeczy: zapisu zapytania z dnia raportu oraz historii danych. Historię zapewniają tabele z wersjonowaniem systemowym, które pozwalają odczytać stan tabeli z wybranej chwili.

SELECT Symbol, Nazwa, StanMinimalny
FROM dbo.Towar
FOR SYSTEM_TIME AS OF '2026-08-31T23:59:59';

Mechanizm opisuje dokumentacja tabel czasowych. Bez niego pozostaje eksport wyniku zapisany w chwili wykonania zapytania.

Jak formułować pytania

Pytanie o określonej strukturze daje bardziej przewidywalne zapytanie. Dobrze sformułowane zdanie zawiera cztery składniki.

  • Miara - co liczyć, na przykład wartość netto albo liczbę dokumentów.
  • Wymiar - według czego grupować, na przykład klient albo miesiąc.
  • Okres - konkretny przedział dat zamiast określeń względnych.
  • Filtr - ograniczenie do jednego statusu albo magazynu.

Szkolenie użytkowników i miejsce działania modelu

Nowy użytkownik potrzebuje krótkiego przeglądu wzorcowych pytań i przykładów błędnych wyników z tabeli kontrolnej. Pomaga w tym asystent użytkownika programu, opisany w artykule o szkoleniach i pomocy kontekstowej. Firmy, które nie chcą przekazywać treści pytań publicznemu modelowi, korzystają z prywatnej instancji w chmurze Azure, omówionej w tekście o ochronie danych w Chat GPT.