Pokazywanie postów oznaczonych etykietą baza danych. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą baza danych. Pokaż wszystkie posty

czwartek, 22 maja 2014

Zestawienia: nieformalnie o podstawach języka SQL - część 3

Trzecia i ostatnia część cyklu poświęconego podstawom języka SQL. W pierwszej i drugiej części stworzyliśmy polecenie SQL, które na podstawie zdefiniowanych kryteriów wyszukuje w bazie danych stanowiska i powiązane z nimi fakty kulturowe (określenie okresu dziejów, jednostki kulturowej, funkcji a także znalezionych śladów działalności człowieka). To polecenie ma następującą postać:

SELECT S.ID, S.OBSZAR_AZP, S.NR_AZP, F.FUNKCJA FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F ON S.ID = F.STANOWISKO WHERE S.OBSZAR_AZP = '55-12' AND F.KULTURA IS NOT NULL AND S.DATA_BADAN >= '2012-01-01' AND S.DATA_BADAN <= '2012-12-31'

Wskazujemy w nim, że w zestawieniu ma znaleźć się identyfikator stanowiska, numer obszaru AZP, numer stanowiska w ramach obszaru AZP - te atrybuty są pobierane z tabeli Stanowiska oznaczonej literą 'S' oraz fukcję stanowiska - informacja zapisana w tabeli FaktyKulturowe. Pod uwagę są brane stanowiska z obszaru '55-12', które mają określoną funkcję i zostały przebadane między 1 stycznia 2012 i 31 grudnia 2012 roku.

Istotą wielu zestawień są obliczenia, które można także wykonywać przy pomocy języka SQL. Służą do tego tak zwane funkcje agregujące. Nazywamy je w ten sposób, ponieważ zasadą ich działania jest to, że zbierają wyniki z wierszy, które spełniają podane warunki i przetwarzają je w określony sposób. 

Przykładem funkcji agregującej jest funkcja COUNT(nazwa_atrybutu), która oblicza ile razy pojawiła się wartość w kolumnie, której nazwę wpisaliśmy między nawiasami okrągłymi (w miejscu oznaczonym jako nazwa_atrybutu). Na przykład, jeżeli w poleceniu SQL użyjemy funkcji COUNT, a w jej parametrze podamy kolumnę id z tabeli stanowiska, to policzy wszystkie wiersze, które mają w tej kolumnie jakąś wartość.

SELECT COUNT(S.ID) FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F ON S.ID = F.STANOWISKO WHERE S.OBSZAR_AZP = '55-12' AND F.KULTURA IS NOT NULL AND S.DATA_BADAN >= '2012-01-01' AND S.DATA_BADAN <= '2012-12-31'

Wynik powyższego polecenia można zinterpretować jako liczbę stanowisk zapisanych w bazie danych z obszaru '55-12', które mają określoną funkcję i zostały przebadane między 1 stycznia 2012 i 31 grudnia 2012 roku. Warto zauważyć, że wynik takiego polecenia będzie zawsze zawierał dokładnie jeden wiersz z jedną kolumną, w które zostanie wyświetlona liczba stanowisk spełniających kryteria.

Innym przykładem funkcji agregującej jest SUM(nazwa_atrybutu), która dodaje do siebie wartości zapisane w kolumnie nazwa_atrybutu. Wartościami tymi muszą być liczby. Możemy jej użyć do obliczenia łącznej liczby fragmentów ceramiki znalezionych na stanowiskach.


SELECT SUM(F.CERAMIKA) FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F ON S.ID = F.STANOWISKO WHERE S.OBSZAR_AZP = '55-12' AND F.KULTURA IS NOT NULL AND S.DATA_BADAN >= '2012-01-01' AND S.DATA_BADAN <= '2012-12-31'

Ponownie wynikiem będzie dokładnie jeden wiersz zawierający sumę wartości z kolumny CERAMIKA w tabeli FaktyKulturowe, w wierszach, które spełniają podane kryteria.

W jednym poleceniu SQL można użyć więcej niż jednej funkcji agregującej. Na przykład:

SELECT COUNT(S.ID), SUM(F.CERAMIKA) FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F ON S.ID = F.STANOWISKO WHERE S.OBSZAR_AZP = '55-12' AND F.KULTURA IS NOT NULL AND S.DATA_BADAN >= '2012-01-01' AND S.DATA_BADAN <= '2012-12-31'

Wynikiem polecenia będzie dokładnie jeden wiersz z dwoma kolumnami. W pierwszej zostanie wyświetlona liczba stanowisk, w drugiej liczba fragmentów ceramiki, które zostały na nich znalezione. Inne przydatne funkcje agregujące to MIN - określa minimalną wartość w kolumnie, MAX - maksymalną wartość w kolumnie, AVG - oblicza średnią wartości w kolumnie.

Ważnym aspektem używania funkcji agregujących jest grupowanie wyników. Wyobraźmy sobie, że chcemy uzyskać liczbę stanowisk zapisanych w bazie danych z podziałem na jednostki kulturowe. W takim wypadku polecenie SQL powinno mieć taką postać:

 
SELECT F.KULTURA, COUNT(S.ID) FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F ON S.ID = F.STANOWISKO GROUP BY F.KULTURA

Na pierwszy rzut oka widać, że polecenie jest bardzo podobne do tych, które opisałem wcześniej. Mamy funkcję agregującą COUNT(S.ID), która policzy wszystkie stanowiska. Oprócz tego mamy atrybut KULTURA, według którego zostaną pogrupowane i policzone stanowiska. Wynik polecenia będzie zawierał dwie kolumny i tyle wierszy, ile różnych kultur zostało znalezionych w tabeli FaktyKulturowe. Do nazwy każdej kultury, w drugiej kolumnie wiersza będzie przypisana liczba oznaczająca liczbę stanowisk reprezentujących jednostkę z kolumny pierwszej.

Oprócz tego w poleceniu pojawiło się wyrażenie GROUP BY F.KULTURA. Używając grupowania w poleceniach sql trzeba jawnie podać, według której kolumny (albo wielu kolumn) mają być agregowane wyniki. Należy pamiętać, że w poleceniach grupujących, między słowami SELECT i FROM mogą znaleźć się tylko funkcje agregujące i nazwy atrybutów, według których są łączone wartości. Te same nazwy atrybutów muszą znaleźć się w wyrażeniu GROUP BY.

W ramach ćwiczenia spróbuj napisać polecenie obliczające średnią ilość fragmentów ceramiki na wszystkich stanowiskach w bazie danych z podziałem na numer obszaru AZP i porównaj to z prawidłową odpowiedzią poniżej.

SELECT S.OBSZAR_AZP, AVG(F.CERAMIKA) FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F ON S.ID = F.STANOWISKO GROUP BY S.OBSZAR_AZP

 

środa, 21 maja 2014

Zestawienia: nieformalnie o podstawach języka SQL - część 2

W poprzednim poście przedstawiłem proste polecenie SQL, które wyświetla wszystkie stanowiska archeologiczne z przypisanymi do nich informacją o funkcji stanowiska (osada, cmentarzysko, itp.). Ma ono następującą postać:

SELECT S.ID, S.OBSZAR_AZP, S.NR_AZP, F.FUNKCJA FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F ON S.ID = F.STANOWISKO

Fragment S.ID, S.OBSZAR_AZP, S.NR_AZP, F.FUNKCJA oznacza, że mają zostać pobrane wartości z następujących kolumn: identyfikator stanowiska, numer obszaru  AZP, numer stanowiska w ramach obszaru AZP, funkcja stanowiska.

Fragment FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F ON S.ID = F.ID oznacza, że polecenie SQL dotyczy tabeli Stanowiska oznaczonej skrótem 'S' oraz tabeli FaktyKulturowe oznaczonej jako 'F'. Spójnik LEFT OUTER JOIN łączący nazwy tabel spowoduje, że nawet jeżeli stanowisko nie ma określonego ani jednego faktu kulturowego, to i tak zostanie wyświetlone (wtedy w miejscu funkcji pojawi się pusta wartość). Z kolei  pisząc ON S.ID = F.STANOWISKO, informujemy, że każdy fakt kulturowy z tabeli FaktyKulturowe, w kolumnie (atrybucie) STANOWISKO ma zapisany identyfikator stanowiska, do którego należy. Aby wyświetlić tylko te stanowiska, które mają conajmniej jeden fakt kulturowy zamiast spójnika LEFT OUTER JOIN należy użyć JOIN (zaznacz aby zobaczyć prawidłową odpowiedź).

Powyższe polecenie wyświetli wszystkie stanowisk oraz ich funkcje zapisane w bazie danych. Natomiast naszym celem jest pobranie tylko stanowisk z obszaru AZP 55-12. Żeby osiągnąć taki efekt, musimy sformułować kryterium, którego spełnienie będzie warunkiem wyświetlenia wiersza z wartościami. W opisanym przpadku zapiszemy je następująco:

WHERE S.OBSZAR_AZP = '55-12'

Oznacza ono tyle, że w każdym wierszu wartość w kolumnie OBSZAR_AZP tabeli Stanowiska zostanie porównana z numerem '55-12'. Jeżeli jest z nim identyczna, to takie stanowisko zostanie uwzględnione w wynikach polecenia. Jeżeli nie - zostanie pominięte.

Możemy rozbudować nasz warunek i dodać drugie kryterium - na liście z wynikami mają znaleźć się tylko stanowiska, które mają określoną jednostkę kulturową (KPL, Kultura łużycka, itp.). Żeby uzyskać taki efekt warunek powinien wyglądać tak:

WHERE S.OBSZAR_AZP = '55-12' AND F.KULTURA IS NOT NULL

Kryterium dotyczące obszaru AZP oraz kultury muszą być obowiązkowo spełnione dla każdego wiersza wyników, dlatego połączyliśmy je operatorem logicznym AND. Gdyby wystarczające byłoby spełnienie tylko jednego z wymagań, to zamiast AND powinniśmy użyć (per analogiam) operatora OR.

F.KULTURA IS NOT NULL (w wolnym tłumaczeniu: "Kultura nie jest nullem") oznacza, że wartość w kolumnie KULTURA w tabeli FaktyKulturowe. W odwrotnej sytuacji, gdyby zależało nam tylko na stanowiskach bez określonej kultury - powinniśmy napisać F.KULTURA IS NULL (tłum. "Kultura jest nullem").

Możemy też użyć innych operatorów matematycznych: większy, mniejszy lub równy, mniejszy, itd. Dodajmy do warunków kolejne kryterium - wszystkie stanowiska powinny być przebadane po 1 stycznia 2012 roku:

WHERE S.OBSZAR_AZP = '55-12' AND F.KULTURA IS NOT NULL AND S.DATA_BADAN >= '2012-01-01'

W rezultacie otrzymaliśmy następujące polecenie:

SELECT S.ID, S.OBSZAR_AZP, S.NR_AZP, F.FUNKCJA FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F ON S.ID = F.STANOWISKO WHERE S.OBSZAR_AZP = '55-12' AND F.KULTURA IS NOT NULL AND S.DATA_BADAN >= '2012-01-01'

Zastanów się jak, wykorzystując dotychczasową wiedzę, zmienić powyższe polecenie,  aby otrzymać tylko stanowiska z okresu między 1 stycznia 2012 i 31 grudnia 2012 i porównaj z prawidłową odpowiedzią (zaczernioną).

SELECT S.ID, S.OBSZAR_AZP, S.NR_AZP, F.FUNKCJA FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F ON S.ID = F.STANOWISKO WHERE S.OBSZAR_AZP = '55-12' AND F.KULTURA IS NOT NULL AND S.DATA_BADAN >= '2012-01-01' AND S.DATA_BADAN <= '2012-12-31'

c.d.n

niedziela, 11 maja 2014

Zestawienia: nieformalnie o podstawach języka SQL - część 1

Przez kilka ostatnich dni intensywnie pracowałem nad mechanizmem zestawień w QAZP2. Kod można już pobrać z repozytorium, ale nowa wersja nie jest wydana. Po tych zmianach użytkownik może definiować zestawienia, a dokładnie rzecz ujmując - polecenia SQL, w sposób bardzo zbliżony do tego, jak się to robi w takich programach jak Microsoft Access, albo LibreOffice Base. Czyli do wyszukania pożądanych informacji teoretycznie nie jest potrzebna znajomość języka SQL, bo wszystkie parametry zestawienia - takie jak wyświetlane kolumny, albo warunki ograniczające wyniki, można wyklikać, przy pomocy graficznego interfejsu użytkownika.

Jednak w praktyce okazuje się, że przy tworzeniu zestawień podstawowa znajomość SQL jest jak najbardziej na miejscu, szczególnie, gdy planujemy utworzenie jakiegość skomplikowanego podsumowania. I o tych podstawach, które moim zdaniem pokrywają 90% potrzeb przeciętnego użytkownika bazy danych, jest ten artykuł.

Wyobraźmy sobie bazę danych w której znajdują się dwie tabele: Stanowiska oraz FaktyKulturowe. Tabela Stanowiska ma następujące kolumny (atrybuty):
  • id - niepowtarzalny identyfikator stanowiska,
  • obszar_azp - nr obszaru AZP,
  • nr_azp - nr stanowiska w ramach obszaru AZP,
  • data_badan - data przeprowadzonych badań.
Tabela FaktyKulturowe zawiera informacje o śladach pobytu człowieka (np. fragmentach ceramiki, narzędziach, itp.) - przesłankach do uznania jakiegoś miejsca za stanowisko archeologiczne i określenia jego chronologii. Tabela ma zdefiniowane następujące kolumny ( atrybuty):
  • id - niepowtarzalny identyfikator faktu
  • stanowisko - identyfikator stanowiska, którego dotyczy fakt. Pojedyncze stanowisko może mieć przypisany jeden lub więcej faktów, lub nie mieć przypisanych faktów wogóle.
  • okres - okres dziejów - na przykład: Neolit, Nowożytność, epoka Żelaza, itd.,
  • kultura - jednostka kulturowa - na przykład Kultura Pucharów Lejkowatych,
  • funkcja - funkcja stanowiska - np. osada, cmentarzysko, obozowisko, itd.
  • liczba fragmentów ceramiki

Szablon polecenia wyszukującego dane w bazie SQL można przedstawić następująco:

SELECT 
      lista_atrybutów 
FROM 
      lista_tabel_oraz_widoków 
[WHERE 
      lista_warunków
[GROUP BY 
      lista_atrybutów_grupujących].

Postawą każdego zestawienia są tabele (conajmniej jedna), które przeszukuje się pod kątem określonych kryteriów i z których pobiera się wartości, które następnie są wyświetlane użytkownikowi. W poleceniu wyszukującym dane (w skrócie w poleceniu SELECT) tabele, które są potrzebne do wykonania zestawienia określa się w miejscu, które oznaczyłem jako lista_tabel_oraz_widoków.

Na takiej liście może znajdować się tylko jedna tabela i wtedy za wyrazem FROM wystarczy wstawić jej nazwę. Sprawa komplikuje się, gdy zestawienie dotyczy więcej niż jednej tabeli. Wyobraźmy sobie przypadek, w którym chcemy wyszukać wszystkie osady na obszarze AZP 55-12. Informacja o funkcji stanowiska jest zapisana w tabeli FaktyKulturowe. A informacja o obszarze AZP, do którego należy Stanowisko w tabeli Stanowiska. Stąd prosty wniosek, że w zestawieniu musimy użyć obu tabel.

Aby to zrobić musimy określić w jaki sposób stanowisko jest połączone z faktami kulturowymi. Tabela FaktyKulturowe ma atrybut stanowisko, który zawsze ma wartość odpowiadającą identyfikatorowi stanowiska, zapisywanego w atrybucie id w tabeli Stanowiska. Dysponując tą wiedzą możemy zbudować listę tabel:

... FROM Stanowiska S JOIN FaktyKulturowe F ON S.ID = F.STANOWISKO ...

W ten prosty sposób przekazaliśmy, że wszystkie potrzebne informacje znajdują się w połączonych tabelach Stanowiska oraz FaktyKulturowe. Tak utworzona lista ma jedną wadę - stanowiska, które nie mają przypisanych faktów kulturowych zostaną pominięte. Czasami ma to uzasadnienie, a czasami nie. Aby spowodować, że Stanowisko zostanie wyświetlone nawet, jeżeli nie ma faktów kulturowych, powyższy fragment należałoby przepisać do takiej postaci:

... FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F ON S.ID = F.STANOWISKO ...

Zamiast pisać JOIN, użyliśmy wyrażenia LEFT OUTER JOIN. Jeżeli dla danego stanowiska nie ma w tabeli FaktyKulturowe takiego wiersza, który by zawierał identyfikator tego stanowiska, to wartości z tabeli Stanowiska i tak zostaną przeanalizowane i ewentualnie wyświetlone. Jeżeli musimy dołączyć kolejne tabele do listy, postępujemy w analogiczny sposób.

W zestawieniu zazwyczaj chcemy wyświetlić tylko informacje z wybranych kolumn tabeli. To, które wartości powinny być wyświetlone w zestawieniu określa się w miejscu, które oznaczyłem jako lista_atrybutów, czyli innymi słowy mówiąc lista nazw kolumn tabeli. 

SELECT S.ID, OBSZAR_AZP, FUNKCJA FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F ON S.ID = F.STANOWISKO

Polecenie powyżej tworzy listę stanowisk, na której każde stanowisko może mieć określoną funkcję. Jeżeli funkcja jest nieokreślona, to pole w zestawieniu będzie puste. Lista atrybutów w zestawieniu jest następująca: S.ID - identyfikator stanowiska, OBSZAR_AZP, FUNKCJA. Skąd wiadomo, że S.ID oznacza identyfikator stanowiska, a nie identyfikator faktu kulturowego. Stąd, że użyliśmy "skrótu" (aliasu) S wymienionego także na liście tabel, gdzie przypisaliśmy literę 'S' do tabeli Stanowiska. 

A jak powinniśmy zażądać w poleceniu SQL wyświetlenia identyfikatora faktu kulturowego? Podświetl zaczerniony fragment aby porównać swoją odpowiedź z prawidłowym rozwiązaniem: F.ID 

Część druga

niedziela, 17 lutego 2013

Golang : obsługa bazy Spatialite

W QAZP2 pojawiła się nowa funkcja, moim zdaniem jedna z ważniejszych, czyli filtrowanie danych. Póki co można ją stosować tylko do stanowisk i polega na wybieraniu z listy stanowisk tych, które spełniają wybrane kryteria. A więc w pierwszym kroku wyszukujemy stanowiska znajdujące się na przykład na pewnym obszarze, a następnie spośród nich stanowiska, które mają dużą wartość dla badań nad przeszłością i wobec których istnieje zagrożenie zniszczenia. To oczywiście tylko przykład, bo praktycznie stanowiska można filtrować w oparciu o większość atrybutów edytowanych w programie. A prezentację sposobu stosowania tego mechanizmu można obejrzeć na krótkim filmie.

To by było na tyle, jeżeli chodzi o ostanie wydarzenia, bo dzisiaj chciałbym dokończyć temat z poprzedniego tygodnia, czyli wykorzystanie języka Go do realizacji projektu QAZP2. Wtedy omówiłem kilka interesujących właściwości Go, które odróżniają go od dobrze znanych C/C++ a także popularnych języków obiektowych, na przykład Javy. Go (Golang) charakteryzuje się bogatą biblioteką standardową, która umożliwia realizację podstawowych zadań, bez konieczności instalacji dodatkowych zależności. W pozostałych przypadkach można się posłużyć intuicyjnym i wygodnym mechanizmem zarządzania nimi. Na przykład do pobrania sterownika do bazy SQLite/Spatialite. Istnieje kilka alternatywnym projektów, w ramach których jest on rozwijany, ja zdecydowałem się na Go-Sqlite3. Jak już pisałem do jego instalacji wystarczy wykonać polecenie
go get github.com/mattn/go-sqlite3
, które po pierwsze pobierze kod źródłowy utrzymywany w repozytorium GIT i po drugie skompiluje go, jeżeli spełnione są wszystkie zależności. To jest proste nie tylko w teorii, z tym że w normalnej postaci sterownika nie można używać do baz przestrzennych. Wynika to z tego, że przed wykonaniem jakiegokolwiek polecenia SQL, które dotyczy danych geometrycznych należy załadować rozszerzenie spatialite, na przykład stosując polecenie (na linuksie) select load_extension('libspatialite.so.3'). Do tego, by ono zadziałało trzeba wcześniej zmodyfikować kod źródłowy sterownika w ten sposób, by w chwili "łączenia się" z bazą umożliwiał ładowanie rozszerzeń, która to możliwość jest domyślnie zablokowana.
Dowolny program może korzystać z bazy SQLite przy pomocy biblioteki funkcji, których używa się na przykład do otwierania bazy, wykonywania poleceń SQL i pobierania wyników. Wśród nich jest także funkcja sqlite3_enable_load_extension, która służy (jak sama nazwa wskazuje ...) do włączenia możliwości ładowania rozszerzeń. Trzeba ją wykonać po otworzeniu pliku z bazą danych. A więc w pliku sqlite3 , wewnątrz funkcji Open, przed deklaracją zwracającą wskaźnik do nowego połączenia z bazą należy wstawić następujące polecenie: 
C.sqlite3_enable_load_extension(db, C.int(1)). I to wszystko. Po otworzeniu bazy zostanie wywołana ww. funkcja, gdzie parametrami są wskaźnik do bazy i liczba 1 oznaczająca True. Po nawiązaniu połączenia wystarczy wykonać polecenie SQL, które spowoduje załadowanie rozszerzenia Spatialite i od tej chwili można przetwarzać dane geometryczne zapisane w przestrzennej bazie danych.

sobota, 9 lutego 2013

Golang : import danych AZP

Jednym z ważniejszych aspektów związanych z projektem QAZP było przygotowanie narzędzia, które (w miarę) bezboleśnie pozwoli na "przerzucenie" danych utworzonych w programie AZPMAX do relacyjnej bazy SQL. Oczywiście pierwszą decyzją był wybór języka programowania, który z wiadomych względów padł na Jave, choćby z tego powodu, że mam sporo doświadczeń związanych z tworzeniem narzędzi obsługujących zarówno DBF-y jak i bazy danych. Niestety musiałem z tego zrezygnować, bo mimo wcześniejszych pozytywnych wyników, tym razem sterownik do SQLite odmówił współpracy z danymi przestrzennymi. Przy próbie inicjalizacji rozszerzenia Spatialite JVM przestawała działać. W związku z tym zdecydowałem się stworzyć narzędzie do importu w języku Golang, który od kilku lat jest rozwijany przez dobrze znaną firmę Google. Ten wybór też ma uzasadnienie, biorąc pod uwagę, że:
  1. mam już pewne doświadczenie związane z tworzeniem oprogramowania w Go,
  2. dobra obsługa baz sql, w tym Sqlite.
Na minus trzeba zaliczyć brak obsługi DBF-ów, ale to póki co rozwiązałem kopiując dane z plików CSV, a do tego formatu można łatwo wyeksportować DBF, posługując się na przykład arkuszem kalkulacyjnym.
Golang / Go ma kilka interesujących właściwości, które przyniosły mu już dużo rozgłosu i powodują, że język dość szybko zyskuje na popularności. Jest statycznie typowany, ale określenie typu zmiennej można przerzucić na kompilator. Wydaje mi się, że nie można go uznać za niskopoziomowy, bo nie umożliwia na przykład wykorzystywanie assemblera, natomiast różnorakie porównania wykazują, że w wielu przypadkach szybkością dorównuje programom pisanym w C / C++. Nad tymi dwoma ma przewagę (przec co niektórych uznawaną za wadę) automatycznego zarządzania pamięcią (garbage collector). Programy można tworzyć w modelu strukturalnym - do reperentowania danych używa się list, map, i struktur bardzo podobnych do tych stosowanych w C / C++, albo takim, który oferuje abstrakcję i polimorfizm (ale bez dziedziczenia) znany z paradygmatu programowania obiektowego. Realizuje się to przy pomocy tak zwanych interfejsów, które nieco przypominają konstrukcje o tej samej nazwie, które stosuje się w Javie. W Golang interfejs jest pewnym kontraktem, który umożliwia tworzenie polimorficznych obiektów. Natomiast inny jest sposób ich stosowania. O ile w Javie, jawnie trzeba podać, że klasa jest implementacją, o tyle w przypadku Golang jest to automatycznie rozpoznawane przez kompilator. Każdy typ danych, dla którego zaimplementowano funkcje wskazane w definicji interfejsu staje się automatycznie jego implementacją. W narzędziu do importu, o którym mowa został zdefiniowany interfejs Tabela z jedną metodą Params, która zwraca tablicę wartości dowolnego typu. Każda struktura, która implementuje interfejs może być zastosowana w metodzie dodaj(ps *sql.Stmt, t Tabela, spr bool), która wykonując metodę Params pobiera wartości ze struktury w takiej kolejności, które powinny być zastosowane jako parametry polecenia SQL reprezentowanego przez inny interfejs Stmt zdefiniowany w bibliotece standardowej Golang.
W tym krótkim opisie trudno wskazać wszystkie właściwości Go, ja skupiłem się tylko na tych, które były istotne z uwagi na problem, który musiałem rozwiązać. W następnym poście opisałem, jak "zmusić" sterownik Go do obsługi danych geometrycznych zapisanych w przestrzennej bazie danych.

niedziela, 30 grudnia 2012

Klasyfikacja kulturowo-historyczna: kodowanie pojęć

W poprzednim artykule opisałem organizację wykazów do kodowania informacji o klasyfikacji kulturowo-historycznej. Dla przypomnienia: na ekranie aplikacji, albo na karcie AZP pokazywane są informacje o tym, z jakiego okresu dziejów (epoka kamienia, średniowiecze, ...), zdaniem archeologa, pochodzi badane stanowisko, jaka była jego funkcja (osada, cmentarzysko, ...), czy sposób i tradycję wykonania znalezionych przedmiotów (kultura pucharów lejkowatych, kultura łużycka, ...). Nazwy wykorzystywane do opisu są przechowywane w tabelach-wykazach: OKRESY_DZIEJOW, JEDNOSTKI, FUNKCJE. Kluczem podstawowym każdej krotki w tabeli wykazu jest ciąg alfanumeryczny kodujący informację, która jest zapisana w rekordzie. W przypadku wykazu funkcji kod ma następującą strukturę ABCCD, gdzie kolejne litery oznaczają jego segmenty. W tym przypadku obowiązkowe jest podanie tylko segmentu A, w którym określa się ogólną funkcję: np. obrzędową literą B, obronną literą O, gospodarczą G, itd. Zatem gdyby w opisie funkcji stanowiska podać kod składający się z litery G, oznaczałoby to tylko tyle, że pełniło ono w przeszłości funkcję gospodarczą. Kolejne segmenty kodu służą do zapisu bardziej szczegółowej informacji. Jeżeli kod S oznacza funkcję sepulkralną (związaną z grzebaniem zmarłych), to SL będzie oznaczało cmentarzysko ciałopalne, SL03 - cmentarzysko ciałopalne płaskie, a SL03J - cmentarzysko ciałopalne płaskie z grobami jamowymi, które to pojęcie jest przykładem najbardziej szczegółowej informacji w wykazie funkcji. Oczywiście sposób konstruowania kodu, opisany na przykładzie funkcji, został zastosowany w wykazie okresów i jednostek kulturowych. 
Jak z każdym kodowaniem informacji, z tym także wiążą się określone zyski i straty, których bilans powinien decydować o jego zastosowaniu do naszego projektu. Do oczywistych zalet oczywiście należy spójna i czytelna reprezentacja informacji w bazie danych. Zamiast w każdym rekordzie charakteryzującym funkcję stanowiska pisać cmentarzysko ciałopalne płaskie z grobami jamowymi można po prostu wstawić kod SL03J i liczyć na to, że będzie on zrozumiały dla odbiorcy. Po drugie znacząco może ułatwić wyszukiwanie informacji o stanowiskach przy pomocy operatora SQL LIKE. Listę stanowisk z funkcją gospodarczą można utworzyć stosując polecenie SELECT * FROM FAKTY WHERE FUNKCJA LIKE 'G%', które uzwględni kod G, GI, GP13 i inne, które pasują do podanego wzorca. Szybkość wykonania takiego polecenia nie jest najwyższa, ale można je zoptymalizować na przykład przez utworzenie odpowiedniego indeksu na kolumnie FUNKCJA. Stosowanie wzorców nie zawsze będzie miało pożądanych skutek, ponieważ np. polecenie SELECT * FROM FAKTY WHERE FUNKCJA LIKE '%04%' zwróci stanowiska z funkcją osada otoczona rowem/rowami (MS04) a także grób szkieletowy katakumbowy (SS04).
Stosowanie kodów wymusza też na autorach pewną ścisłość w charakteryzowaniu stanowisk. Można to postrzegać jako zaletę, gdyż podwyższa to wartość bazy jako źródła archeologicznego, a także jako wadę, gdyż procesy społeczne nie zawsze można wtłoczyć w zmatematyzowany schemat. Jeżeli wykaz można rozszerzać o nowe pojęcia, to w przypadku, gdy brakuje odpowiedniego, można je dodać do tabeli. Natomiast w przypadku, gdy uzupełnianie wykazu jest niemożliwe, to trzeba próbować wykorzystać już istniejące, co może budzić oczywisty sprzeciw wobec utraty informacji.

sobota, 10 listopada 2012

Przepis na PostGIS



Podstawą działania QAZP2 jest przestrzenna baza danych. Informacje o śladach działalności człowieka, a także o prowadzonych badaniach archeologicznych łączy się z przestrzenią po przez dodanie do nich współrzędnych geograficznych. W przypadku archeologii jest to kluczowa informacja, gdyż umożliwia analizę danych w kontekście krajobrazu i środowiska, które prawdopodobnie miały niebagatelny wpływ na decyzje podejmowane przez dawnych osadników.


Zanim przejdę do omówienia schematu bazy danych, do którego dostosowany jest QAZP2, w kilku zdaniach opiszę przygotowywanie systemu PostgreSQL do pracy z bazami przestrzennymi. Nie zamierzam szczegółowo omawiać poszczególnych poleceń - lepiej ode mnie robi to bogata dokumentacja. Zgodnie z tytułem - to ma być przepis, który szybko pozwoli upiec ciastko ;). Będę pisał z perspektywy użytkownika Linuksa, a dokładnie Debiana i zakładam, że czytelnicy posiadają podstawową wiedzę na temat posługiwania się tym systemem.

1. Instalacja i konfiguracja PostgreSQL


# apt-get install postgresql postgis

To polecenie chyba nie wymaga komentarza. Po kilku albo kilkunastu minutach oczekiwania (w zależności od prędkości połączenia) aktualna wersja oprogramowania - czyli system PostgreSQL i jego rozszerzenie Postgis powinno znaleźć się na dysku i zostać zainstalowane i uruchomione.

# cd /etc/postgresql/{wersja}/main/
# cp pg_hba.conf pg_hba.bak
# echo "local all all password" >> /etc/postgresql/{wersja}/main/pg_hba.conf
# /etc/init.d/postgresql restart

W domyślnej konfiguracji z bazą może się połączyć użytkownik root albo specjalnie tworzony do tego celu postgresql. A więc przed nawiązaniem połączenia z bazą i wykonaniem polecenia SELECT, UPDATE, CREATE itp. należy zalogować się jako root albo postgresql (ten drugi ma zdefiniowane domyślne hasło postgresql). Osobiście wolę inne podejście, w którym użytkownika i hasło podaje się w chwili łączenia z bazą danych. Ma ono dodatkowe uzasadnienie: w sytuacji, w której będziemy próbowali się połączyć w QGIS z bazą jako inny użytkownik, na przykład milosz, w domyślnej konfiguracji Postgre odrzuci nasze rządanie. Dlatego do pliku pg_hba.conf dodajemy następujący wiersz local all all password (trzecia linia). Żeby zabezpieczyć się przed uszkodzeniem tego pliku, wcześniej (druga linia) należy wykonać jego kopię zapasową. Ta operacja wymaga zrestartowania systemu, co czyni polecenie w lini czwartej.


To jest najprostsza konfiguracja, jaką można sobie wyobrazić, ale w zupełności wystarcza do lokalnego testowania i korzystania z systemu PostgreSQL.

# createuser -U postgresql -W -d -P tester

Na koniec tworzymy nowego użytkownika o nazwie tester, który będzie uprawniony do tworzenia nowych baz danych (przełącznik -d). Przełącznik -P oznacza, że w chwili tworzenia użytkownika zostaniemy poproszeni o nadanie mu hasła. Przełączniki -U i -W są standardowe dla każdego polecenia PostgreSQL. Pierwszym wskazujemy tego użytkownika, jako który łączymy się do bazy w celu utworzenia nowego - w tym przypadku tym użytkownikiem jest wspominany już postgresql; użycie drugiego spowoduje, że przed dodaniem nowego, trzeba będzie podać hasło użytkownika postgresql.

2. Tworzenie bazy PostGIS

# createdb -O tester -h localhost -p 5432 -U tester -W azp2
# createlang plpgsql -U tester -W azp2

Kiedy nowy użytkownik jest już dodany, czas na utworzenie bazy danych. W pierwszym poleceniu tworzymy nową bazę o nazwie azp2, której właścicielem będzie tester (przełącznik -O). Po wykonaniu drugiej komendy w bazie azp2 będzie można tworzyć procedury w języku PL/SQL. Przełączniki -W i -U mają takie samo znaczenie, jak przy dodawaniu nowego użytkownika.

# cd /usr/share/postgresql/{wersja_postgre}/contrib/postgis-{wersja_postgis}
# psql -f postgis.sql -U tester -W azp2
# psql -f spatial_ref_sys.sql -U tester -W azp2

Na koniec do bazy azp2 trzeba dodać procedury, których używamy np. do dodawania współrzędnych, wyszukiwania na ich podstawie, itd., które są zaimplementowane w języku PL/SQL, o którym pisałem powyżej. Polecenia, które dodają wspomniane procedury znajdują się w katalogu utworzonym w chwili instalacji Postgis. Przechodzimy do niego w pierwszej lini, a następnie wywołujemy narzędzie psql podając jako parametr plik postgis.sql, który zawiera definicje procedur SQL. Do pracy z przestrzenną bazą danych przydatne będzie także dodanie definicji systemów odniesienia, które są używane do konwertowania współrzędnych. To także robimy za pomocą polecenia psql podając jako parametr plik spatial_ref_sys.sql.
I tyle. Baza przestrzenna jest gotowa do pracy. Od tej chwili można już tworzyć tabele ze współrzędnymi geograficznymi. Ale o tym innym razem.