Kochany pamiętniczku, mam problem.

Mój prawie-pierwszy SELECT i od razu takie coś… MS SQL. Proszę nie dławić się ze śmiechu [zbyt głośno], to poważna rzecz jest.

SELECT
Str(Year(sesje.RCS_TS_Start), 4) As rok,
Replace(Str(Month(sesje.RCS_TS_Start), 2), ' ', '0') AS miesiac,
bazy.Baz_Nazwa AS klient,
Count(nag.TrN_RazemNetto) AS ile_razy,
Sum(DateDiff(hour, sesje.RCS_TS_Start, sesje.RCS_TS_Stop)) AS godz,
Sum(nag.TrN_RazemNetto) AS netto

FROM CDN.RCSesje sesje INNER JOIN
CDN.Operatorzy oper On oper.Ope_OpeID = sesje.RCS_OpeId
INNER JOIN
(CDN.Bazy bazy
INNER JOIN
( [SERVER\OPTIMA].FABRYKA.CDN.Kontrahenci kth
INNER JOIN [SERVER\OPTIMA].FABRYKA.CDN.TraNag nag ON kth.Knt_KntId =
nag.TrN_OdbID) ON bazy.Baz_GUID = kth.Knt_BazaBR_GUID )
ON bazy.Baz_BazID = sesje.RCS_BazId AND Month(sesje.RCS_TS_Start) =
Month(nag.TrN_DataDok) AND Year(sesje.RCS_TS_Start) = Year(nag.TrN_DataDok)

WHERE bazy.Baz_Nazwa = 'Jola'

GROUP BY
Str(Year(sesje.RCS_TS_Start), 4),
Replace(Str(Month(sesje.RCS_TS_Start), 2),' ', '0'),
bazy.Baz_Nazwa

ORDER BY
Str(Year(sesje.RCS_TS_Start), 4), Replace(Str(Month(sesje.RCS_TS_Start), 2),' ', '0')

Dwa tematy:

  1. Dla klienta ‚Jola’ w tabeli ‚sesje’ mam w jednym przypadku 4 zapisy i tylko jeden zapis w tabeli nag. SELECT w powyższym kształcie znajduje grzecznie ten zapis dla każdego wyszukanego wiersza w tabeli ‚sesje’, skutkiem czego agregat sumuje mi wielkości z pola TrN_RazemNetto do kwoty czterokrotnie większej, niż rzeczywista.
  2. Z kolei w innym przypadku mam bliżej nieokreśloną ilość kwalifikowalnych wierszy w tabeli ‚sesje’ i dwa spełniające warunek wiersze w tabeli ‚nag’. Sumuje mi tylko jeden. Złączenia wydają się być poprawne pod kątem business logic.

Co robię źle, kochany pamiętniczku?

9 myśli w temacie “Kochany pamiętniczku, mam problem.

  1. Przede wszystkim źle robisz, że strugasz jedno wielkie zapytanie, zamiast pobrać dane paroma prostymi zapytaniami i obrobić skryptem. To, że prawie wszystko można zrobić na poziomie SQL-a, nie znaczy, że to ma sens – choćby z takiego powodu jak wyżej, masz jakiś błąd i weź go w tym czymś znajdź. A w skrypcie byś go pewnie znalazł raz dwa.

    Poza tym… cztery INNER JOINy? Z czego dwa zagnieżdżone? Nie wiem, może MS SQL ma wydajniejsze JOINy niż MySQL, ale i tak nie wierzę, żeby to było znośne wydajnościowo.

    I jeszcze jedno – czy w MS SQL nie można zrobić „GROUP BY rok, miesiac ORDER BY rok, miesiac”, zamiast powtarzać po trzy razy te przekształcenia?

    Polubienie

  2. Normalnie nie zajmuję się SQLem. Wszystko to, co piszesz, zapewne jest słuszne, ale potrzebuję napisać tylko jedną kwerendę i tyle. Jeśli nie znajdę błędu, zgrzytnę zębami i pewnie skończy się na skrypcie…
    Wydajność nie jest problemem w tym przypadku, zapytanie odpalę raz na miesiąc. Swoją drogą albo bazy mam nieszczególnie wielkie, albo na serwerze z innych względów nie robi to szczególnego wrażenia 😉 zapytanie [z powyższymi babolami] trwa wszystkiego jakieś pół minuty.
    Przy okazji… znasz / znacie może jakieś normalne maszynki do robienia kostek OLAP z MSSQL, za które nie musiałbym zastawić domu i potomstwa do dziesiątego pokolenia? Używam Bi-Lite Cube-it Zero, ale żaden tester tego chyba na oczy nie widział.
    Group by / order sprawdzę, dzięki.

    Polubienie

  3. Nie widzę,żebyś gdziekolwiek używał tabeli oper. Ja spróbował bym zmienić FROM na:

    FROM CDN.RCSesje sesje
    INNER JOIN
    (CDN.Bazy bazy
    INNER JOIN
    ( [SERVER\OPTIMA].FABRYKA.CDN.Kontrahenci kth
    INNER JOIN [SERVER\OPTIMA].FABRYKA.CDN.TraNag nag ON kth.KntKntId =
    nag.TrN
    OdbID) ON bazy.BazGUID = kth.KntBazaBRGUID )
    ON bazy.Baz
    BazID = sesje.RCS_BazId

    Polubienie

  4. @Cichy
    Silniki baz danych są m.in. po to właśnie, żeby wydajnie obsłużyć JOINY. I z reguły(*) robią to dobrze a zapytania SQL są dość wygodne w pracy na danych (no chyba, że dochodzimy do podzapytań, tego niecierpię). I z reguły nie widzę sensu angażowania w tym celu dodatkowej obróbki skryptem. No chyba, że mamy już jakiegoś frameworka a tabele są zmapowane w nim do obiektów – ale i to nie zawsze ma sens.

    (*) dochodzi do tego odpowiednie zaprojektowanie samego zapytania SQL, zaprojektowanych indeksów i innych mechanizmów optymalizacji bazy danych.

    Polubienie

  5. @takieGadanie

    Silniki baz danych są m.in. po to właśnie, żeby wydajnie obsłużyć JOINY.

    Teoretycznie. W praktyce bywa z tym różnie. Przez lata pracy z tabelami po fafnaście milionów rekordów, nauczyłem się używać JOINów tylko w ostateczności i po uważnym obwąchaniu.

    a zapytania SQL są dość wygodne w pracy na danych

    Spróbuj zdebugować to „dość wygodne” zapytanie powyżej. I pomyśl potem, o ile prostsze by było zdebugowanie skryptu robiącego to samo w bardziej przejrzysty sposób.

    Polubienie

  6. Na studiach (czyli dawno) bawiliśmy się w takie różne testy. Odpowiednie przebudowanie długaśnego zapytania na wiele tabel plus np. inne ustawienie indeksów potrafiło dać niezły wzrost wydajności. I takie zapytanie bywało lepsze niż cała seria pojedynczych zapytań robiących to samo. Ale jak pisałem to było dawno i na Oraclu (perspektywy szczególnie zmaterializowane ułatwiały życie). Teraz, przyznaję nie mam okazji bezpośrednio pracować z dużymi bazami.

    Jak pisałem, przy podzapytaniach dla mnie sytuacja się komplikuje. Wtedy faktycznie wolę rozbić to na mniejsze fragmenty i część operacji na danych przerzucić na skrypt – o ile jest to część aplikacji, gdzie mam dostęp do stosownego API. Poza tym zawsze można zastosować perspektywy, które ułatwiają lekturę skomplikowanych zapytań.

    Polubienie

  7. Widywałem takie zapytania w poprzedniej pracy. Teoretycznie znam SQLa (podstawy właściwie), raz robiłem nawet procedurę oraclowej bazie. Ale takich kawałków kodu unikam i unikać będę. Sam napisałeś, że wydajność nie jest problemem, zatem zrób 3 proste zapytania i obrób w Pythonie. Przynajmniej będzie się to dało czytać bez odruchu wymiotnego. 😉

    Poprawka: w sumie, to źle napisałem. Zapytania, które widywałem w pracy mają z powyższym tylko tyle wspólnego, że mam ochotę rzygać na ich widok. Tamte jednak byłem w stanie zanalizować i zorientować się, co robią (na szczęście nie musiałem pisać (zbyt wielu…)). Tego Twojego to nawet przeczytać się nie da. I polskie nazwy między angielskimi operatorami, choć skądinąd irytujące, są tu najmniejszym problemem. Na przykład wcięcia kodu. Wiele by pewnie nie pomogły, ale na pewno by też nie zaszkodziły.

    Polubienie

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj /  Zmień )

Zdjęcie na Google

Komentujesz korzystając z konta Google. Wyloguj /  Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj /  Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj /  Zmień )

Połączenie z %s