Projektowanie baz danych wykład 1. Podstawowe pojęcia, architektury systemów baz danych Baza danych jest magazynem danych z nałożoną na niego wewnętrzna strukturą. Danych używa się do opisu realnie istniejących osób, przedmiotów, czynności, zdarzeń, zjawisk. System Zarządzania Bazą Danych (SZBD) jest zorganizowanym zbiorem narzędzi umożliwiającym zdefiniowanie struktury bazy, jej utworzenie i później wykonywanie wszystkich operacji na bazie. Popularna jest nazwa angielska SZBD: DataBase Management System (DBMS). W zależności od modelu danych stosowanego w konkretnym systemie mówi się o relacyjnych SZBD (RSZBD lub RDBMS), zorientowanych obiektowo SZBD (ZOSZBD lub OODBMS), obiektowo relacyjnych SZBD (ORSZBD lub ORDBMS). System Bazy Danych to SZBD oraz baza danych. Często (zwłaszcza potocznie) system bazy danych nazywany jest po prostu bazą danych. SZBD może posiadać wbudowane narzędzia umożliwiające tworzenie aplikacji użytkownika, tj. programów, za pomocą których użytkownik łączy się i obsługuje bazę. Zarządzanie bazą danych obejmuje m.in.: organizowanie struktury danych, wprowadzanie danych, wyszukiwanie danych według zadanych kryteriów, usuwanie danych, aktualizowanie danych, zachowanie integralności (ochronę przed błędami, które utrudniłyby wykonywanie różnych operacji), administrowanie bezpieczeństwem, organizowanie pracy wielodostępnej, łączenie i wymianę danych z innymi systemami baz danych, zarządzanie transakcjami, tj. złożonymi operacjami, które muszą się albo wszystkie wykonać poprawnie, albo żadna z nich (anulowanie transakcji musi zakończyć się wycofaniem wszelkich dokonanych już zmian i doprowadzeniem danych do stanu sprzed rozpoczęcia transakcji). Wiele systemów umożliwia wykonywanie transakcji rozproszonych. Transakcje. Zbiór operacji, które muszą się wszystkie wykonać poprawnie. Gdyby co najmniej jedna z operacji objętych transakcją się nie powiodła, wszystkie zostaną wycofane. O transakcjach będzie jeszcze w dalszych wykładach. Architektury współczesnych systemów baz danych. Podział ANSI: Architektura trójschematowa – warstwa aplikacji, warstwa koncepcyjna i warstwa fizyczna. 1 Projektowanie baz danych wykład 1. Architektura klient-serwer we współczesnych dużych systemach baz danych klient jest programem żądającym obsługi pewnego zlecenia, serwer jest programem odbierającym od klienta żądanie i wykonującym je. Architektury dwuwarstwowe, trójwarstwowe i wielowarstwowe. Dwuwarstwowa – jedna warstwa to system bazy danych, druga to oprogramowanie klienckie. W architekturze trójwarstwowej (lub uogólniając – wielowarstwowej) między serwerem bazy danych a programem klienckim jest program pośredniczący, na przykład serwer WWW. Architektury rozproszone Dane są przechowywane na różnych serwerach, ale są wzajemnie dostępne (przynajmniej część z nich). Bazy rozproszone można tworzyć na przykład aby przybliżyć serwer do klienta. O bazach rozproszonych, technikach ich tworzenia będzie osobny wykład. Popularne współczesne systemy zarządzania bazami danych oparte o architekturę klient-serwer: Oracle (http://www.oracle.com/), jeden z dwóch najpopularniejszych w Polsce systemów komercyjnych, Microsoft SQL Server (http://www.microsoft.com/), jeden z dwóch najpopularniejszych w Polsce systemów komercyjnych, PostgreSQL (http://www.postgresql.com/), istnieje bardzo popularna wersja darmowa na Linuxa, system jest znacznie prostszy w porównaniu z pierwszymi dwoma, posiada za to pewne rozszerzenia obiektowe, MySQL (http://www.mysql.com/) – istnieje bardzo popularna wersja darmowa na Linuxa, system jest prostszy od PostgreSQL, pewne ważne mechanizmy stosowane w bazach relacyjnych nie są zaimplementowane, jednak jest to system bardzo szybki, czasami dorównujący nawet Oracle czy SQL Serwerowi, DB2 firmy IBM, w wielu testach uznawany za najszybszy, Sybase (http://www.sybase.com/). Charakterystyka systemów typu desktop. Jednym z najbardziej znanych systemów typu desktop jest MS Access. Baza danych oraz oprogramowanie klienckie stanowi tu integralną całość. Nie ma serwera, który odpowiadałby na pytania klientów, praca sieciowa jest realizowana na zasadzie wspólnego dostępu do plików. Architektury danych. Model relacyjny, relacyjno obiektowy i zorientowany obiektowo. Więcej o tych modelach będzie w dalszych wykładach. W tej chwili większość systemów zarządzania bazami danych to systemy relacyjne bądź relacyjno obiektowe. Dla systemów relacyjnych powstała teoria umożliwiająca m.in. zalgorytmizowanie procesu projektowania bazy danych. Podstawy projektowania systemów relacyjnych, m.in. proces normalizacji tabel będzie omówiony na jednym z wykładów. 2 Projektowanie baz danych wykład 1. Podstawy SQL – część 1. Structured Query Language jest językiem zapytań strukturalnych w relacyjnych bazach danych. Powstanie pierwszych wersji SQL’a datuje się na drugą połowę lat siedemdziesiątych - w laboratorium IBM w San Jose w Kaliforni. Opracowany został pierwotnie dla DBMS o nazwie DB2. Jest to język nieproceduralny, w którym określamy co chcemy uzyskać a nie jakim sposobem. SQL jest określany jako język 4 generacji (4GL). Istnieje więcej języków 4GL operujących na bazach danych, np. SAS 4GL firmy The SAS Institute. Powszechnie przyjęte standardy SQL: The American National Standards Institute (ANSI): SQL-89, SQL-92 (SQL2), SQL-99 (SQL3). SQL jest tylko teoretycznie niezależny od systemów operacyjnych, sprzętu i DBMS. W praktyce różne firmy zbliżają się do standardów, ale oferują swoje rozszerzenia i modyfikacje. Przykłady: Oracle PL/SQL, Microsoft SQL Server Transact SQL, Microsoft Access Jet SQL, PostgreSQL Pg/SQL. Składnia SQL będzie prezentowana z wykorzystaniem podanych niżej zasad. Przyjęte konwencje zapisu składni zdań SQL: Słowa kluczowe – pisane dużymi literami. Jednak sam SQL nie jest czuły na wielkość liter. Elementy, które mogą, ale nie muszą wystąpić – w nawiasach prostokątnych []. Elementy, z których można wybrać jeden – rozdzielone pionową kreską |. Gdy jeden z elementów z listy musi wystąpić, lista ujmowana jest w nawiasy klamrowe {}. Przy prezentacji składni stosowana będzie składnia przyjmowana w MS SQL Serwerze. W zdaniach SQL wyróżnia się części (frazy) rozpoczynające się od pewnych słów kluczowych, np. SELECT, FROM, WHERE itd. Połączone frazy tworzą zdanie. Mówi się również o klauzulach SELECT, FROM, WHERE itd. Zapytanie wybierające – zdanie SELECT ... FROM .... Wybór danych z jednej tabeli. SELECT [ALL | DISTINCT] { * | [[nazwatabeli.]pole_1 [AS nazwa_1] [, [[nazwatabeli.]pole_2 [AS nazwa_2] [,...] ] } FROM nazwa_tabeli [AS nowa_nazwa] 3 Projektowanie baz danych wykład 1. [ [ [ [ WHERE wyrażenie ] GROUP BY lista_pól ] HAVING wyrażenie ] ORDER BY kryterium1 [, kryterium2 [,...] ] ]; Całe wyrażenie można wpisać w jednej linii, wcięcia zastosowane są tylko dla zwiększenia przejrzystości. W wielu systemach zdanie SELECT ... FROM ... zakończone jest średnikiem. Najprostszy przykład: SELECT * FROM Pracownicy; Gwiazdka oznacza wybranie wszystkich pól z tabeli. Ponieważ nie zdefiniowano kryterium wyboru, to wyświetlone zostaną wszystkie rekordy. Wybieranie pól. SELECT Nazwisko, Imię, [Data urodzenia] FROM Pracownicy; lub SELECT Pracownicy.Nazwisko, Pracownicy.Imię, Pracownicy.[Data urodzenia] FROM Pracownicy; Uwaga: wiele systemów nie akceptuje w SQL polskich liter i odstępów w nazwach. Powyższa konstrukcja pochodzi z MS SQL Serwera. Nazwa pola zawierająca odstęp ujmowana jest w nawiasy prostokątne. Zmiana nazwy pola w zapytaniu (tzw. alias). SELECT Nazwisko, Imię, [Data urodzenia] AS Data FROM Pracownicy; Następuje zmiana nazwy pola Data urodzenia na Data (tylko na potrzeby tego zapytania). W niektórych wersjach SQL stosowanie słowa AS nie jest konieczne. Wybór niepowtarzających się rekordów. SELECT (oznacza to samo, co SELECT ALL) może wyświetlić powtarzające się wiersze, np. SELECT [Kod działu] FROM Pracownicy; SELECT Miejscowość FROM Pracownicy; Zdanie to zwróci tyle rekordów ile ich jest w tabeli pracownicy. Zatem kody działów będą się powtarzać, podobnie miejscowości. Aby wyświetlić wiersze bez powtórzeń (nazwy działów, czy miejscowości, w których mieszkają pracownicy) należy użyć słowa kluczowego DISTINCT: SELECT DISTINCT [Kod działu] FROM Pracownicy; SELECT DISTINCT Miejscowość FROM Pracownicy; 4 Projektowanie baz danych wykład 1. Pola wyliczeniowe. SELECT Nazwisko, Imię, Stawka, Stawka*0.115 AS Premia, Year(Date()) – Year([Data zatrudnienia]) AS [Lata pracy] FROM Pracownicy W polach wyliczeniowych występują wyrażenia. W wyrażeniach SQL można stosować nazwy pól, operatory, nawiasy (wymuszają kolejność wykonywania obliczeń), stałe i funkcje (zależne od systemu). Operatory arytmetyczne: + - * / % (% oznacza dzielenie modulo, czyli resztę z dzielenia całkowitego dwóch liczb). Uwaga: w stałych liczbowych znakiem rozdzielającym część całkowitą liczby od ułamkowej jest kropka! Operatory porównania: > większe od < mniejsze od >= większe bądź równe <= mniejsze bądź równe, <> lub != nie równe = równe. Przy pomocy operatorów porównania konstruuje się wyrażenia, zwracające wartości logiczne (PRAWDA lub FAŁSZ, TRUE, FALSE) lub wartość NULL pole nieuzupełnione – puste). Operatory działające na tekstach w polach wyliczeniowych: operatory porównania, operator łączenia (konkatenacji): +, ewentualnie ||, lub &. Przykład. SELECT Nazwisko + ‘ ‘ + Imię As Osoba FROM Pracownicy; tworzy jedną kolumnę zatytułowaną osoba. Teksty w SQL ujmowane są w pojedyncze apostrofy (w wielu systemach również w podwójne). Kryteria wyboru rekordów. Kryterium wyboru rekordów w prostym zapytaniu wybierającym definiowane jest we frazie WHERE. Kryterium wyboru to wyrażenie logiczne. Używa się w nim m.in. operatorów: porównania, operatora IN (lista wartości), Between ... And... operatorów logicznych AND, OR, NOT, XOR operatora LIKE (do tekstów). IS NULL (pole jest puste – nie wypełnione) 5 Projektowanie baz danych wykład 1. IS NOT NULL (pole nie jest puste) Operator LIKE oznacza dopasowanie napisu do wzorca. We wzorcach można używać znaków specjalnych (tzw. metaznaków). Nie ma w implementacjach SQL jednolitego standardu metaznaków. Np. w MS Access * oznacza dowolny ciąg znaków, jednak częściej - w innych implementacjach używa się znaku % . Jeden dowolny znak jest zapisywany jako _ (podkreślenie) (w MS Access jest to znak „?”) Kryterium wybierające osoby o drugiej literze nazwiska L wyglądałoby tak: Nazwisko LIKE ‘_L%’ lub: Nazwisko LIKE ”?L*” Przykłady zapytań w SQL z kryterium wyboru rekordów: SELECT Nazwisko, Imię, [Data zatrudnienia] FROM Pracownicy WHERE Nazwisko LIKE 'K%'; SELECT Nazwisko, Imię, [Data zatrudnienia] FROM Pracownicy WHERE Stawka > 1000; SELECT Nazwisko, Imię, Telefon FROM Pracownicy WHERE Telefon IS NULL; SELECT Nazwisko, Imię, Telefon FROM Pracownicy WHERE Telefon IS NOT NULL; Użycie operatorów AND, OR, NOT, XOR. Poniższe tabelki przypominają rezultat operacji z użyciem wymienionych operatorów dla argumentów a i b. OR (LUB), alternatywa (suma logiczna) a OR b a Prawda Prawda Fałsz Fałsz b Prawda Fałsz Prawda Fałsz a OR b Prawda Prawda Prawda Fałsz AND (I) koniunkcja (iloczyn logiczny) a AND b 6 Projektowanie baz danych wykład 1. a Prawda Prawda Fałsz Fałsz b Prawda Fałsz Prawda Fałsz a AND b Prawda Fałsz Fałsz Fałsz NOT (NIE) zaprzeczenie NOT a a Prawda Fałsz NOT a Fałsz Prawda XOR różnica symetryczna a XOR b a Prawda Prawda Fałsz Fałsz b Prawda Fałsz Prawda Fałsz a XOR b Fałsz Prawda Prawda Fałsz Przykład: Wybierz osoby o stawce między 1000 a 1500 zł. SELECT Nazwisko, Imię, Stawka FROM Pracownicy WHERE Stawka >= 1000 AND Stawka <=1500; Uwaga na wartości NULL w wyrażeniach. Jeśli w wyrażeniu arytmetycznym któraś z wartości jest NULL, wówczas wynik całego wyrażenia przyjmuje się NULL (w większości implementacji SQL). Jeśli którymś z argumentów operatorów porównania jest wartość NULL, wówczas przyjmuje się, że wynik jest UNKNOWN (nieznany). Przyjęto tu logikę trójwartościową, w której oprócz wartości PRAWDA (TRUE) i FAŁSZ (FALSE) jest jeszcze wartość NIEZNANY (UNKNOWN). W praktycznych implementacjach UNKNOWN jest reprezentowany też jako NULL. Przyjęto następujące działanie operatorów logicznych z wartościami NULL: Prawda OR NULL = Prawda 7 Projektowanie baz danych wykład 1. Fałsz OR NULL = NULL Prawda AND NULL = NULL Fałsz AND NULL = Fałsz Sortowanie – ORDER BY. SELECT Imię, Nazwisko, [Data zatrudnienia], Stawka FROM Pracownicy ORDER BY Nazwisko, Imię; Kryterium sortowania może zawierać nazwę pola i słowa ASC (ascending = rosnąco) lub DESC (descending = malejąco). SELECT Imię, Nazwisko, [Data zatrudnienia], Stawka FROM Pracownicy ORDER BY Stawka DESC; SELECT Imię, Nazwisko, [Data zatrudnienia], Stawka FROM Pracownicy ORDER BY Stawka DESC, Nazwisko, Imię; Standardowo przyjmowane jest ASC (i nie trzeba tego słowa pisać). Można zamiast nazwy kolumny podać jej numer (numerowanie rozpoczyna się od 1 od lewej strony), jest to wygodne zwłaszcza przy sortowaniu według pól wyliczeniowych. Zapytania podsumowujące. Do tworzenia podsumowań używane są tzw. funkcje agregujące. Jeśli w zdaniu SELECT na liście po słowie kluczowym SELECT pojawi się jedna z niżej podanych funkcji agregujących, wówczas jeśli nie ma frazy GROUP BY, to wszystkie rekordy stanowią jedną grupę, dla której w wybranym polu (polach) wykonywane jest odpowiednie wyliczenie. COUNT - liczy rekordy, które w danym polu podanym jako argument mają wartość różną od NULL. Zapis COUNT(*) wymusza liczenie wszystkich rekordów (bez względu na wartości pól). SUM - sumuje wartości w polu (kolumnie), AVG – oblicza średnią arytmetyczna w polu, MAX – zwraca największą wartość w polu, MIN – zwraca najmniejszą wartość w polu, FIRST – podaje wartość pola w pierwszym rekordzie w grupie, LAST – podaje wartość pola w pierwszym rekordzie w grupie, VARIANCE – wariancja, STDEV – odchylenie standardowe. W wyniku realizacji takiego zdania wyświetlany jest jeden rekord – z odpowiednimi wyliczeniami (agregacjami). Przykład. SELECT AVG(Stawka) AS [Średnia stawka], MAX(Stawka) AS [Stawka maksymalna],MIN(Stawka) AS [Stawka minimalna] 8 Projektowanie baz danych wykład 1. FROM Pracownicy Zdanie to powoduje wyświetlenie średniej, maksymalnej i minimalnej stawki obliczonej na podstawie danych z pola stawka ze wszystkich rekordów. Grupowanie w podsumowaniach – GROUP BY: Jeśli w zdaniu z podsumowaniami (tzn. z funkcjami agregującymi) jest klauzula GROUP BY, wówczas wszystkie rekordy dzielone są na grupy o jednakowych wartościach w polu podanym po słowach GROUP BY. Odpowiednie wyliczenie wykonywane jest oddzielnie dla każdej tak wyznaczonej grupy rekordów. Przykład - wyświetlenie średniej, maksymalnej i minimalnej stawki w działach: SELECT [Kod działu], AVG(Stawka) AS [Średnia stawka], MAX(Stawka) AS [Stawka maksymalna],MIN(Stawka) AS [Stawka minimalna] FROM Pracownicy GROUP BY [Kod działu]; Następuje tu grupowanie rekordów o jednakowych wartościach wpisanych do pola [Kod działu]. Operacje agregujące są wykonywane w grupach. Jeśli po słowach GROUP BY umieszczona jest lista pól oddzielonych przecinkami, wówczas jest wyznaczana grupa według pierwszego (od lewej strony) pola na liście, każda grupa dzielona jest na podgrupy według drugiego pola listy itd. Odpowiednie obliczenia wykonywane są zawsze tylko dla najmniejszych wyznaczonych w ten sposób podgrup. Wybór grup – klauzula HAVING: SELECT Pracownicy.[Kod działu], Avg(Pracownicy.Stawka) AS [Średnia stawka], Max(Pracownicy.Stawka) AS [Stawka maksymalna], Min(Pracownicy.Stawka) AS [Stawka minimalna] FROM Pracownicy GROUP BY Pracownicy.[Kod działu] HAVING [Kod działu] IN( 'AD','MG'); Klauzula HAVING służy do wyboru grup (a więc działa już po dokonaniu grupowania). Klauzula WHERE działa przed grupowaniem! Przykład: SELECT Pracownicy.[Kod działu], Avg(Pracownicy.Stawka) AS [Średnia stawka], Max(Pracownicy.Stawka) AS [Stawka maksymalna], Min(Pracownicy.Stawka) AS [Stawka minimalna] FROM Pracownicy WHERE [Data zatrudnienia] < '01-01-1990' GROUP BY Pracownicy.[Kod działu] HAVING [Kod działu] IN( 'AD','MG'); Przykład: SELECT Pracownicy.Kod_działu, AVG(Pracownicy.Stawka) Średnia_stawka, MAX(Pracownicy.Stawka) Stawka_maksymalna, MIN(Pracownicy.Stawka) Stawka_minimalna 9 Projektowanie baz danych wykład 1. FROM Pracownicy GROUP BY Pracownicy.Kod_działu HAVING MAX(Stawka) >1300; Łączenie tabel w zapytaniach. W najprostszym przypadku za łączenie tabel na zasadzie połączenia wewnętrznego odpowiedzialna jest klauzula WHERE. Przykład: SELECT Pracownicy.Nazwisko, Pracownicy.Imię , [Zwolnienia chorobowe].[Początek zwolnienia] , [Zwolnienia chorobowe].[Koniec zwolnienia] , [Koniec zwolnienia]-[Początek zwolnienia] AS [Liczba dni] FROM Pracownicy, [Zwolnienia chorobowe] WHERE [Pracownicy].[Nr pracownika] = [Zwolnienia chorobowe].[Nr pracownika]; Jeśli klauzula WHERE w powyższym zapytaniu nie wystąpiłaby, wówczas byłby utworzony zbiór rekordów na zasadzie iloczynu kartezjańskiego (każdy rekord z Pracownicy łączony z każdym rekordem ze Zwolnienia chorobowe) Proste łączenie dwóch tabel we frazie FROM tabela1 {[INNER] | LEFT [OUTER] | RIGHT [OUTER] | FULL} JOIN tabela2 ON tabela1.pole1=tabela2.pole2 Warunek łączenia może być bardziej skomplikowany (łączenie przez dwa lub więcej pól – użycie operatora AND w klauzuli ON). Istnieje możliwość łączenia wielu tabel (dodaje się klauzule JOIN). Uwagi: Słowo OUTER jest opcjonalne i na ogół się je pomija (lub wręcz w ogóle nie można go stosować). Np. LEFT OUTER = LEFT. Podobnie słowo INNER można pominąć i zamiast INNER JOIN pisze się JOIN. Połączenie INNER (wewnętrzne) powoduje wybranie (i połączenie) tylko rekordów, które mają „odpowiedniki” w drugiej tabeli. Połączenia OUTER (zewnętrzne) wybierają wszystkie rekordy z jednej tabeli i dołączają „odpowiadające” z drugiej. W wielu systemach nie ma połączenia typu FULL OUTER (wybieranie z obu tabel wszystkich rekordów). Przykład. Połączenie tabeli Pracownicy i Zwolnienia_chorobowe można zapisać z wykorzystaniem rozbudowanej frazy FROM: 10 Projektowanie baz danych wykład 1. SELECT Pracownicy.Nazwisko, Pracownicy.Imię , Zwolnienia_chorobowe.Początek_zwolnienia , Zwolnienia_chorobowe.Koniec_zwolnienia , Koniec_zwolnienia – Początek_zwolnienia Liczba_dni FROM Pracownicy INNER JOIN Zwolnienia_chorobowe ON Pracownicy.Nr_pracownika = Zwolnienia_chorobowe.Nr_pracownika ORDER BY Pracownicy.Nazwisko, Pracownicy.Imię; Klauzula JOIN może być również wykorzystana do tworzenia połączeń na zasadzie iloczynu kartezjańskiego. Jest to tzw. złączenie krzyżowe – CROSS JOIN. ... FROM tabela1 CROSS JOIN tabela2; Rozbudowana fraza FROM. FROM tabela1 {INNER| LEFT [OUTER] | RIGHT [OUTER] |FULL} JOIN ( tabela2 {INNER| LEFT [OUTER] | RIGHT [OUTER] |FULL} JOIN [(] tabela3 [{INNER| LEFT [OUTER] | RIGHT [OUTER] |FULL} JOIN[(] tabelax [{INNER| LEFT [OUTER] | RIGHT [OUTER] |FULL} JOIN ... )] ON tabela3.pole3a=tabelax.polex)] ON tabela2.pole2a=tabela3.pole3b)] ON tabela1.pole1=tabela2.pole2; Istnieje tu możliwość łączenia wielu tabel. Uwagi: Słowo OUTER można pominąć. Np. LEFT OUTER = LEFT Połączenie INNER (wewnętrzne) powoduje wybranie (i połączenie) tylko rekordów, które mają „odpowiedniki” w drugiej tabeli. Połączenia OUTER (zewnętrzne) wybierają wszystkie rekordy z jednej tabeli i dołączają „odpowiadające” z drugiej. W wielu systemach nie ma połączenia typu FULL OUTER (wybieranie z obu tabel wszystkich rekordów). Wykorzystanie grupowania i łączenia tabel. Przykład. Należy utworzyć zapytanie, które dla każdego pracownika wyliczy ile w sumie był na zwolnieniu chorobowym. Na górze zestawienia powinny być dane o osobach, które były najdłużej na zwolnieniu. SELECT Pracownicy.Nazwisko, Pracownicy.Imię, Sum([Koniec zwolnienia]-[Początek zwolnienia]) AS [Liczba dni] FROM Pracownicy LEFT OUTER JOIN [Zwolnienia chorobowe] ON Pracownicy.[Nr pracownika] = [Zwolnienia chorobowe].[Nr pracownika] GROUP BY Pracownicy.Nazwisko, Pracownicy.Imię ORDER BY Sum([Koniec zwolnienia]-[Początek zwolnienia]) DESC; 11 Projektowanie baz danych wykład 1. Uwaga. Jeśli sortowanie dotyczy kolumny obliczanej, należy powtórzyć wzór wyliczenia jak w powyższym przykładzie, lub wpisać numer kolumny, jak poniżej: SELECT Pracownicy.Nazwisko, Pracownicy.Imię, Sum([Koniec zwolnienia]-[Początek zwolnienia]) AS [Liczba dni] FROM Pracownicy LEFT OUTER JOIN [Zwolnienia chorobowe] ON Pracownicy.[Nr pracownika] = [Zwolnienia chorobowe].[Nr pracownika] GROUP BY Pracownicy.Nazwisko, Pracownicy.Imię ORDER BY 3 DESC; Istnieje możliwość formatowania pola tak, by wartości NULL były wyświetlane jako zero. W tym celu należy użyć odpowiedniej dla konkretnego systemu funkcji formatującej. 12