Archiwum

Posts Tagged ‘sql’

Małe narzędzie do przetwarzania plików

31/03/2012 Dodaj komentarz

Przetwarzanie plików, ich zawartość, tak aby na ich podstawie stworzyć coś innego.
Najczęściej jeśli pliki są niewielkie robi się to ręcznie. Jeśli są trochę większe to można zaprzęgnąć do tego jakieś dobry edytor tekstu. A czasem trzeba napisać narzędzie do tego.
Poniżej zamieszczam prosty skrypt do przetwarzania plików, będzie służył jako baza do tworzenia podobnych narzędzi w przyszłości.

print "start"

class Paczka:
    tabela = ''
    nazwa = ''
    kolumna = ''

    def build(self):
        return 'CREATE INDEX '+self.nazwa+' ON '+self.tabela+' ('+self.kolumna+');'

file = open('indeksy.txt','r+')
data = file.readlines()
file.close()

paczka = Paczka()
for line in data:
    if ("Foreign Key:" in line):
        paczka.nazwa = line.split("Foreign Key:")[1].strip()
    if ("On Table:" in line):
        paczka.tabela = line.split("On Table:")[1].strip()
    if ("Columns:" in line):
        paczka.kolumna = line.split("Columns:")[1].strip()
    if ("Columns:" in line):
        print paczka.build()
        paczka = Paczka()

print "koniec"

Jak można wyczytać program ma wyprodukować zbiór instrukcji tworzących indeksy. Robi to na podstawie pliku, zawierającego wskazania w których miejscach można poprawienia wydajności bazy. Przykład poniżej:

! Foreign Key: SCHEMAT.FK_TABELA_1
!   On Table:  TABELA_1
!   Columns:   KOLUMNA_1

! Foreign Key: SCHEMAT.FK_TABELA_2
!   On Table:  TABELA_2
!   Columns:   KOLUMNA_3

! Foreign Key: SCHEMAT.FK_TABELA_3
!   On Table:  TABELA_3
!   Columns:   KOLUMNA_3

Program jest prosty, widać co jest robione. Usprawnienie co do pierwotnej wersji to wydzielenie klasy agregującej dane. Daje to odseparowanie togo co ma powstać od etapu przetwarzania pliku. Można by się pokusić jeszcze o uogólnienie kodu przetwarzającego poszczególne linie (pozbycie się if_ów). Jednak w tym przypadku byłby to już przerost formy nad tym co ma być wykonane. Może kiedyś uaktualni się ten wpis.

Kategorie:Inne Tagi: , ,

Pisanie procedur składowanych w hibernate

20/01/2012 Dodaj komentarz

Taka koncepcja urodziła mi się dziś: pisanie procedur składowanych w JPA – tutaj zawężam do implementacji Hibernate, a z baza można wskazać Oracle, ale to bez znaczenia.
Nie chodzi o wywoływanie procedur składowanych poprzez JPA czy zwykłe JDBC. Nie chodzi tu także o pisanie w javie ciała procedury składowanej (co na Oraclu da się).
To o co mi chodzi to wprowadzenia pewnej magicznej transformacji, która z kodu javy operującym na encjach JPA wygeneruje odpowiedni kod procedury (w tym wypadku PL/SQL) i stworzy taki kod na bazie. Na javie mogłoby wyglądać to tak:

	@AsStoredProcedure
	public void addCats(Long ownerId, Date date){
		List<Cat> cats = em.createQuery(
		    "select cat from Cat as cat where cat.birthdate < ?1")
		    .setParameter(1, date, TemporalType.DATE)
		    .getResultList();
		Owner owner em.find(Owner.class, ownerId);
		owner.getCats().addAll(cats);
	}

Stojąca za tym idea mogłaby być taka: jeśli wskazana metoda (tutaj oznaczoną odpowiednią adnotacją) może być prze-konwertować poprawnie na odpowiedni ciąg instrukcji PL/SQL, to zebrać to w jedną procedurę składowaną i stworzyć takie coś na bazie. Następnie w miejsce samej metody wstawić odpowiednie proxy, które to będzie wołane zamiast oryginalnej procedury.

Za i przeciw
Dlaczego tak się bawić?
Główna kwestia to wydajność. Stosowanie JPA często i tak kończyło się na wybiegach w postaci procedur składowanych dla poprawienia wydajności. To jednak wprowadzało rozbicie kodu na dwa miejsca, o które trzeba dbać i uaktualniać wraz ze zmianami.
Oczywiści można z tym żyć. Jeśli nałoży się odpowiedni reżim dbania o kod, większość błędów się nie prześliźnie. Jednak ze swojego doświadczenia widzę że jeśli jakiś kod można prosto napisać w JPA to się tak robi. Łatwiej jest to zrobić niż stworzyć analogiczną procedurę na bazie (w końcu programujemy w javie, a nie w sql :)), a to nie zawsze prowadzi do wydajnego kodu (zwłaszcza kiedy realizuję on scenariusz: wyciągnięcia wielkiej kolekcji danych, prostych zmian na jej elementach, podłączenie wszystkiego do innej encji i na koniec utrwalenie całości).
Dlatego też zamarzyło mi się właśnie coś takiego, co sprawiłoby że pisanie prostych procedur byłoby przezroczyste.

Wadą tu jest wprowadzenie dodatkowej abstrakcji (ktoś w debugu mógłby się zastanawiać dlaczego ten kod się nie wykonuję), ale to można by załatwić wprowadzając przemyślane API.

Sposoby realizacji
Realizacja tego jest możliwa, w końcu w logach widać sql który jest faktycznie wykonywany. Więc wystarczy go wziąć, trochę sparsować, opakować w kod tworzący procedurę, zarejestrować to na bazię, i wygenerować odpowiedni zamiennik w postaci proxy, który to będzie wołany zamiast metody w javie.
Najbardziej problematyczne może być pisanie w kodzie tak aby było to zamieniane na odpowiedni kod, pewnie nie każde wyrażenie z PL/SLQ może być zrealizowane.

Co do wgrywania procedur składowanych to jest taki mechanizm opis tutaj, poprzez zwykłe executeUpdate.

Kategorie:Java Tagi: , , ,

Jak to z błędem było: prezentowanie wyników wyszukiwania, a stosowanie if

05/09/2011 Dodaj komentarz

Ostatnio natknąłem się na pewien rodzaj błędu. Związany on jest z wyszukiwaniem; gdzie sytuacja jest prosta jak budowa cepa :): podanie kryteriów, wykonanie zapytania, i zaprezentowanie wyników użytkownikowi. Błąd ten jest o tyle złośliwy, ponieważ pojawia się tylko w części przypadków, dlatego też gdy już zostanie wprowadzony trudniejsze jest jego wykrycie.

Geneza jego jest następująca:
Na początek mamy typową wyszukiwarkę, schemat całego postępowania można przedstawić na poniższym obrazku.

Tak więc użytkownik definiuje kryteria wyszukiwania; co przekłada się następnie na odpowiednie zapytanie do bazy. Wynik, z wykonania zapytania, pakowany jest do obiektów (DTO), których to zbiór służy do wygenerowania prezentacji dla użytkownika.

Za pierwszym razem wszytko działa jak należy, ale klient życzy sobie pewnych modyfikacji. W prezentowanych wynikach, w niektórych przypadkach dla zwracanych rekordów wartość X jest nieprawidłowa (jest pusta). Związane jest to z brakiem odpowiednich danych w bazie. Braki te nie są zmartwieniem klienta, chodzi o prezentacje. Dlatego też, życzy on sobie, aby prawidłową wartość X dla tego przypadku pozyskiwać na podstawie innych wartości. Co w kodzie kończy się taką zmianą:

  public String getWartoscX() {
    if (wartoscX == null) {
      // tutaj poprawka
      return wartoscA + wartoscB;
    }
    return wartoscX;
  }

Zmiana jest wprowadzana; teraz wartości w każdym przypadku prezentują się poprawnie; zgłoszenie uznaję się za rozwiązany ;). Jednak wkradł się już błąd. Tutaj łatwiej go zauważyć: kryterium X dla modyfikowanych przypadków nie jest już poprawne. Jak już wspomniałem podmiana wartości X nie występuje zawsze, powiedzmy w 10% zwracanych rekordów, dlatego też błąd ten może istnieć długo zanim zostanie odkryty.

Dlaczego taka sytuacja powstała?
Oczywiście tutaj łatwiej widać absurdalność wykonania tej poprawki. W tym przypadku odpowiednim podejściem może być chociaż wprowadzenie widoku na bazie, co mogłoby wyglądać tak:

select NVL(wartoscX, wartoscA || wartoscB) as wartoscX, wartoscY, wartoscZ from tabela;

Jednak celem moim jest zastanowienie się dlaczego taka sytuacja mogła zajść (Skłoniło mnie do tego znalezienie w ostatnim czasie kliku przypadków takiego błędu):

  • pierwszą i główną przyczyną jest specyficzność przypadku, który dotyczy niewielkiego wycinka danych. Prowadzi to do tego, że osoba wykonująca poprawkę, może się błędnie sugerować prostotą rozwiązania jakie trzeba zastosować, i nie zastanawiać się bardziej nad jego prawidłowością.
  • drugą przyczyną jest złe skupienie uwagi co do miejsca w którym należy rozwiązać błąd. Nie na prezentacji wyników (tam umieszczać logikę rozwiązania), ale na zapytaniu i zwracanych wynikach. Powodem tego może być opis samego zgłoszenia, gdzie całość jest przedstawiona w łopatologicznej formie: „weź wartość A i wartość B; i dodaj do siebie”. Prawie jak poprawka literówki :).
  • kolejna sprawa to sugerowanie się domyślnym językiem używanym w pracy. Jak ktoś programuje w javie to łatwiej jest mu rozwiązać problem w tym języku, niż silić się na prace w sql.
  • jeszcze jeden powód to złożoność kodu, skutkujący tym że poprawiający ma trudność w uchwyceniu całego kontekstu. W przypadkach, które znalazłem, było to związane z dużym zamieszaniem w przekazaniu obiektów z danymi. Nie były to proste DTO, ale obiekty encji JPA przepakowane do pośrednich obiektów przy użyciu skomplikowanych reguł.

Ogólna rada na przyszłość to: jeśli poprawka dotyczy wycinka pewnego zbioru przyjrzeć się lepiej rozwiązaniu, traktować to bardziej podejrzliwie niż w przypadku gdy zmiany obejmują cały zbiór. Potrzeba wprowadzenia if_a powinna zapalać czerwone światełko :). Skłaniać do zastanowienia się czy w dobrym miejscu ten if jest umieszczany i czy znane są wszystkie miejsca w których powinien on być.
A co do wyszukiwarek dobrze jest rzeźbić je w sql, i nawet jeśli grozi to mnożeniem bytów, stosować szyte na miarę DTO (a nie kombinować z ponownym użyciem innych klas). Także nie umieszczać logiki biznesowej w prezentacji :).

Kategorie:Java Tagi: , , , ,

Błędy związane z czasem

20/03/2011 Dodaj komentarz

Nazbierało mi się ostatnio trochę ciekawych przypadków/błędów związanych z czasem. Są one trochę inne niż te które zazwyczaj spotykam. Mają one tą szczególną cechę że pojawiają się naglę, mimo że nic się w strukturze kodu nie zmieniło; lub występują, a potem znikają (duch).
Przytoczone tu przypadki są związane z bazą danych (może lista się rozszerzy z czasem, a może ktoś podrzuci inne przykłady). Są to błędy związane raczej z danymi, stąd może dlatego częściej pojawiają się właśnie przy bazie.

Przypadek przeterminowania się danych
Objawy: Coś nagle przestało działać, a nic nie było ruszane.
Przykład: Sprawdzenie czy dziś nie mija jakaś data. Warunek ten może być zaszyty w jakimś zapytaniu, widoku czy procedurze. Wszystko jest dobrze na początku, gdy owa graniczna data jest odległa w czasie, powiedzmy za rok. Kiedy jednak to nadchodzi, może być zdziwienie, chwila zamieszania i długotrwałe przeszukiwanie całego kodu, a baza często stoi jako jeden z ostatnich etapów tego szukania.
Nie chodzi tu tylko o przypadek zaszycia sprawdzenia czasu z obecną data (SYSDATE), ale może to być jakaś wartość wzięta z bazy, którą ktoś nieopacznie zmienił, a związek z błędem może nie być łatwo dostrzegalny.

Przypadek niewłaściwego formatu czasu
Objawy: Brak działania funkcjonalności w określonych porach.
Przykład: Narzucenie dostępności czegoś w określonych dniach, czy godzinach.
Błędy gdy zegar systemowy rozjedzie się z rzeczywistością. Bardziej prawdopodobne na etapie developmentu – kiedy nie dba się zbytnio o środowisko i nikomu nie przeszkadza że zegar spieszy się przykładowo o 2 godziny.
Określenie dnia tygodnia (ten właśnie przypadek natchną mnie do napisania tego posta 😉 ). Nie właściwa instalacja bazy i przyjęcie formatu daty, lokalizacji serwera w innej strefie czasowej. A to może prowadzić, że na jednej instancji bazy funkcja może zwracać inne wartości niż na drugiej. Ów przykład dla Oracla
TO_CHAR (SYSDATE, 'D')
określenia jaki dzień tygodnia mam – raz może się to liczyć od niedzieli, innym razem od poniedziałku.

Kategorie:Inne Tagi: ,

Poszukiwanie narzedzia

17/10/2010 Dodaj komentarz

Na początku była potrzeba tworzenia dokumentacji, zostało to opisane tutaj. Miało to jednak dalszą kontynuacje.

Wraz z przejściem do projektu (trwał on już od jakiegoś czasu) stanąłem przed wyzwaniem zapoznania się z bazą danych (jedną z wielu). Dokładniej miałem do rozpoznania jej wycinek, aby przygotować kilka zapytania. I tu okazało się, że to co kiedyś stworzyłem w innym projekcie na potrzeby klienta/administratora może być teraz pomocne dla mnie. Było to sposób produkcji dokumentu przedstawiającego relacje pomiędzy tabelami w bazie. W zamierzeniu miała to być pomoc do zaznajomienia się z baza, w sam raz na początek, gdy obraz jej nie jest jeszcze utrwalony w głowie. Czytaj dalej…

Kategorie:Inne Tagi: , , ,

Dokumentacja struktury bazy

25/08/2010 Dodaj komentarz

Trochę o generowaniu dokumentacji, czyli dobrze kiedy znaczną jej cześć można właśnie wygenerować.
Miałem za zadanie stworzyć dokumentacje struktury bazy dla klienta. Mogłem zakasać rękawy i mozolnie pokrótce opisać każdą z tabel oraz jej powiązania z innymi tabelami (dokument głównie to miało zawierać). Całość przy około 250 tabelach mogło mi zająć około 3 dni :). Ponieważ nie było to naglące i miałem trochę luzu postanowiłem poszukać sposobów na automatyzacje tego. Ostatecznie zajęło mi to ponad 5 dni 😉 (ale nie w całości poświęconych temu), no ale dzięki temu wykonanie ewentualnej aktualizacji jest teraz o wiele szybsze. Czytaj dalej…