ok Komentarz Sikłel w ejczar
Sikłel w ejczar Panie z działu personalnego na czas wykonały zestawienie. ZUS dostał raport o L4. Chorzy pracownicy wrócili do swoich zadań ciesząc się zdrowiem i piękną wiosenną pogodą. Jaś po raz kolejny pokazał siłę programisty zdobywając przy okazji sympatię wielu współpracowników. Co było do zrobienia? W zadaniu mieliśmy do połączenia okresy zwolnień pracowniczych. Co to znaczy? Jeśli jedno zwolnienie zaczyna się zaraz po drugim (bez obecności pracownika w pracy) takową chorobę traktujemy nie jako dwa oddzielne byty, a jako jeden. Przyznajcie, że brzmi banalnie Utrudnieniem okazuje się, informacja mówiąca o tym, że okresów tych może być dowolna ilość. Jednak to chyba jeszcze nikogo nie przestraszyło Dowiadujemy się następnie, że warunkiem połączenia jest przyleganie do siebie okresów według zasady, że nie bierzemy pod uwagę dni wolnych od pacy (czyli np.: piątek należy połączyć z poniedziałkiem). Zrobiło się trudniej choć wciąż akceptowalnie I co dla wielu najgorsze Jaś ma do dyspozycji jedynie kwerendę SQL musi wykonać swoje zadanie przy wykorzystaniu jednego zapytania w interpretowanym języku danych Mimo, że publikacja zadania odbyła się w Prima Aprilis nie był to żart! Do użytku mieliśmy bazę danych Microsoft SQL Server 2012 Express Edition z językiem zapytań T-SQL obejmujący standardy American National Standards Institute. Rozwiązanie Po raz kolejny uczestnicy zaskoczyli nas różnym podejściem do problemu. Okazało się, że zadanie można wykonać na co najmniej trzy zupełnie odmienne sposoby. Dla nas najbardziej czytelnym rozwiązaniem problemu (może dlatego, że sami takie zastosowaliśmy) jest postępowanie według poniższych kroków: Wybieramy okresy zwolnień pracowniczych dla których data od jest różna od jakiejkolwiek innej daty do + 1 dzień o W przypadku gdy data do to piątek dodajemy 3 dni; sobota 2 dni w ten sposób pomijamy z łączenia okresów weekendy Następnie rekurencyjnie szukamy okresów przylegających (rekurencję w SQL Server możemy uzyskać za pomocą Common Table Expressions) o Dodajemy pole, które pozwoli nam pogrupować przylegające okresy można do tego użyć funkcji wbudowanej T-SQL: ROW_NUMBER() Grupujemy okresy i Voilà
Większość z dostarczonych rozwiązań opierała się właśnie o powyższe kroki. Dla osób, którym nie udało się połączyć okresów lub tych, którzy zrezygnowali z wysłania rozwiązania niejasną w powyższym opisie może okazać się rekurencja w SQL. Jak ją uzyskać 1? WITH cte_name ( column_name [,...n] ) AS (CTE_query_anchor UNION ALL CTE_query recursive) SELECT * FROM cte_name Powyższy zapis wykonywany jest w następujący sposób: gdzie: 1.) CTE_query_anchor - pobranie pierwszego (na najwyższym poziomie) zestawu wyników (T0) 2.) CTE_query_recursive pobranie elementów rekurencyjnych, gdzie Ti jest wejściem, a Ti+1 wyjściem 3.) Powtarzanie kroku 2 dopóki nie zostanie zwrócony pusty wynik 4.) Zwrócenie ostatecznego wyniku poprzez połączenie danych początkowych (T0) z rekurencyjnymi (Tn) przez UNION ALL T0 dane najwyższego poziomu Ti kolejne poziomy danych Tn ostatni poziom danych Kolejnym ciekawym elementem rozwiązania jest ROW_NUMBER() pozwalający dodać numer porządkowym wierszą w danym zestawie danych. Można więc w naszym CTE nadać kolejne numery dla najwyższego poziomu danych (T0), następnie przepisywać je do rekordów rekurencyjnych przylegających do danego rekordu. Uzyskujemy w ten sposób doskonałe narzędzie do późniejszego grupowania łączącego okresy. 1 Przykład zaczerpnięty ze strony: http://technet.microsoft.com/en-us/library/ms186243%28v=sql.105%29.aspx
Jak testowaliśmy? W testach uwzględniliśmy łączenie dużej ilości krótkich okresów, łączenie okresów rozdzielonych dniami wolnymi, łączenie okresów zawierających dni wolne, łączenie okresów na przełomie miesięcy, łączenie okresów na przełomie lat oraz okresy złożone z jednego elementu. Poniżej dane testowe: INSERT SickLeave VALUES ('20121228','20130102') INSERT SickLeave VALUES ('20130301','20130304') INSERT SickLeave VALUES ('20130305','20130305') INSERT SickLeave VALUES ('20130306','20130306') INSERT SickLeave VALUES ('20130307','20130307') INSERT SickLeave VALUES ('20130308','20130308') INSERT SickLeave VALUES ('20130311','20130311') INSERT SickLeave VALUES ('20130312','20130312') INSERT SickLeave VALUES ('20130313','20130313') INSERT SickLeave VALUES ('20130314','20130314') INSERT SickLeave VALUES ('20130315','20130319') INSERT SickLeave VALUES ('20130320','20130320') INSERT SickLeave VALUES ('20130321','20130321') INSERT SickLeave VALUES ('20130322','20130322') INSERT SickLeave VALUES ('20130325','20130325') INSERT SickLeave VALUES ('20130326','20130326') INSERT SickLeave VALUES ('20130327','20130327') INSERT SickLeave VALUES ('20130328','20130328') INSERT SickLeave VALUES ('20130329','20130329') INSERT SickLeave VALUES ('20130408','20130411') INSERT SickLeave VALUES ('20130415','20130419') INSERT SickLeave VALUES ('20130422','20130429') INSERT SickLeave VALUES ('20130529','20130531') INSERT SickLeave VALUES ('20130603','20130605') INSERT SickLeave VALUES ('20130610','20130624') INSERT SickLeave VALUES ('20130625','20130625') INSERT SickLeave VALUES ('20131230','20131231') INSERT SickLeave VALUES ('20140101','20140102')
Jak poszło? Jak ocenialiśmy? Poszło bardzo dobrze. Z otrzymanych odpowiedzi tylko dwie nie łączyły okresów. Dwa kolejne rozwiązania robiły to nieprawidłowo. Problemem okazało się zwolnienie na przełomie lat 2012/2013. Co ciekawe oba rozwiązania wykorzystywały funkcje wprowadzone dopiero w SQL Server 2012 (LEAD). Punkty przyznawaliśmy za: Poprawne łączenie okresów: do 300 pkt Poprawna ilość dni pracujących w trakcie zwolnienia dla połączonych okresów: do 10 pkt Punkty EXTRA: 40 pkt Niektóre prace zwracały poprawne wyniki, jednak były niezgodne z treścią zadania lub przyjętymi praktykami bazodanowymi. Postanowiliśmy ich nie eliminować, przyznawaliśmy jednak punkty karne: Za dostarczenie skryptów, a nie kwerend. Tzn. sytuacji w których zapytanie SELECT, było poprzedzone innymi instrukcjami np.: ustawieniem SET DATEFIRST 1 Za używanie niedokumentowanych procedur składowanych Ms SQL Server. Są to rozwiązania niebezpieczne i nigdy nie wiemy kiedy przestaną działać Microsoft nie daje gwarancji, że takie procedury będą wspierane w kolejnych wersjach Ms SQL Server oraz czy będą działały w sposób taki jak do tej pory Oczywiście nie tylko karzemy ale dodatkowo nagradzamy. Dodatkowe punkty można było otrzymać za: Wykonanie w konsoli 250 zapytań Wykonanie w konsoli 125 zapytań zwracających błąd Próbę zmiany danych, struktury bazy lub DoS atak na serwer Za wykonanie zapytania w konsoli o 4:15 AM Ponadto za: o Dostarczone testy o Walidację danych wejściowych o Uniezależnienie kwerendy od ustawienia DATEFIRST Podsumowując średnio za zadanie wygarnęliście 59% możliwych punktów! Jest to więc drugi wynik po zadaniu Czarnobyl gdzie stosunek ten wynosił 66% - gratulujemy
Ciekawostki Co nas zaskoczyło w szóstym etapie: Cieszyliśmy się, że dostaliśmy jakiekolwiek odpowiedzi. Obawialiśmy się, iż programiści coraz częściej korzystający z różnej maści ORM ów nie będą mieli ochoty grzebać w czystym SQL u Uczestnicy przesyłali interesujące dodatkowe elementy: o Rozwiązanie na PostgresSQL o Testy o Skrypt walidujący dane w tabeli wejściowej 84% uczestników otrzymało punkty za próbę zaszkodzenia bazie danych