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