SQL

Temat: Wprowadzenie do języka zapytań SQL: wybór danych (SELECT), tworzenie tabeli (CREATE), modyfikacje danych w tabeli: INSERT, DELETE, UPDATE. Modyfikacja struktury.


SQL - Structured Query Language - strukturalny język zapytań
Język SQL jest uznawany za standard języka bazy danych. Jest oferowany przez niemal wszystkie komercyjne i prototypowe relacyjne bazy danych. Przyjmuje się również w obiektowych bazach danych jako uzupełnienie używanych w nich języków imperatywnych (np. C++, Smaltalk) o mechanizamy deklaratywnego manipulowania na danych. Język SQL jest językiem wysokoego poziomu do manipulowania danymi o niewielkiej liczbie instrukcji a o dużych możliwościach.

Są różne odmiany języka dostosowane do systemów baz danych. Język SQL różni się w szczegółach, w zależności od tego z jakiego pakietu narzędziowego korzystamy.

Przykady: SQL Plus firmy Oracle, SQL w Delphi, MS SQL Access.

SQL * Plus jest zaproponowanym przez firmę Oracle rozszerzeniem języka SQL.

W Delphi można w sposób bezpośredni używać lokalnego języka SQL dla baz danych w standardzie dBASE i Paradox oraz języka SQL dla baz danych tworzonych w systemie Borland Local InterBase Server. Język SQL dla baz danych w standardzie dBase lub Paradox jest nieco ograniczoną wersją języka w porównaniu z wersją stosowaną sla baz danych systemu Local InterBase. Podsystem Local InterBae umożliwia testowanie na jednym komputerze aplikacji, które następnie mogą by uruchamiane w środowisku klient/serwer. Architektura klient/serwer polega na tym, że serwer przetwarza dane i wykonuje zapytania do bazy danych. Natomiast poszczególne stacje lokalne (klienci) zlecają serwerowi do wykonania pewne operacje i otrzymują gotową odpowiedź. Syste Local InterBase Server jest wyposażony w 2 podsytemy o nazwach Windows ISQL oraz Server Manager. Windows ISQL słuzy do interaktywnej pracy w języku SQL, a system Server Manager jest wykorzystywany do administrowania bazami danych na serwerze.

Microsoft Access SQL

Microsoft Access jest przedstawicielem systemów zarządzania relacyjnymi bazami danych (Relational Data Base Management System - RDBMS). Przymiotnik relacyjny, świadczący o nadążaniu za powszechnym trendem w dziedzinie baz danych, pochodzi od matematycznej teorii relacji. Na jej podstawie sformułowano podstawowe założenia teorii relacyjnych baz danych. Model relacyjny dominuje nad innymi, np. hierarchicznym, sieciowym - pozwala bowiem najpełniej realizować nadrzędny cel każdego systemu zarządzania bazą danych - wyciągać informację z danych.

MS Access jest poliglotą, jeśli chodzi o języki manipulowania danymi, zna bowiem zarówno język Query by Example (QBE), jak i SQL - strukturalny język zapytań. QBE jest bardziej metodą tworzenia zapytań i pasuje do filozofii Windows, natomiast SQL jest zbliżony do języka formalnego.

Kiedy tworzymy zapytanie korzystając z szablonu QBE (przy pomocy myszy), Access równocześnie konstruuje odpowiadające mu wyrażenie w języku SQl. Chociaż na pierwszy rzut oka SQL wydaje się zbędny, to jest to jednak złudne. Po pierwsze SQL pozwala na zdefiniowanie zapytań, których nie da się stworzyć metodą tradycyjną, po drugie wyrażeń SQL można użyć w szablonie QBE jako tzw. zapytań zagnieżdżonych, a także przy określaniu niektórych atrybutów formularzy i jako argumentów niektórych czynności realizowanych przez makropolecenia. W szczegóności SQl jest niezbędny do zdefiniowania zapytania będącego źródłem danych dla wykresu na formularzu.

SQL to kilkadziesiąt słów kluczowych i kilkanaście podstawowych konstrukcji składniowych. Formalnie podobny do języków wysokiego poziomu, lecz od nich prostszy.

Zwykle, niezależnie od tego, czy użytkownik definiuje zapytanie za pomocą jezyka SQL, czy korzystając z szablonu QBE, Access twozry jego odpowiednik, posługując się metodą komplementarną. Wyjątek stanowią tzw. zapytania SQL-właściwe (SQL-Specific Query), których nie da się stworzyć przez szablon QBE.


Zapytania wybierające - SELECT

Polecenie select jest najczęściej wykonywanym poleceniem języka SQL. Umożliwia wyszukiwanie informacji w bazie danych przy użyciu operatorów algebry relacji. W swojej minimalnej formie, umożliwiającej projekcję danych pojedynczej relacji, musi zawierać:

Przykłady:
1) Wyświetlić wszystkie dane z tablicy EMP(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) - PRACOWNIK(NR, NAZWISKO, ETAT, NR_KIEROWNIKA, DATA_ZATR, ZAROBEK, PROWIZJA, NR_DZIAŁU)

SELECT * FROM EMP;

2) Wyświetlić identyfikator zespołu i nazwę z tablicy ZESPÓŁ(ID_ZESP,NAZWA,ADRES):

SELECT id_zesp, nazwa FROM zespół;

Ogólna postać zapytania wybierającego:

W SQL Oracle

SELECT [DISTINCT | ALL] * | column | expr [ [AS] c_alias } ["column header"], | table.* | table.column
[[AS] c_alias] ["column header"],...
FROM table [t_alias] ..
[WHERE condition]
[ORDER BY {expr | c_alias | position} [ASC | DESC] [, {expr | c_alias | position} [ASC | DESC]]..}

W MS Access SQL

[PARAMETERS
parametr_1 typ_danych [, parametr_2 typ_danych ...];]
SELECT [ALL | DISTINCT | DISTINCTROW | TOP n [PERCENT]]
{* | [nazwa_tabeli.]pole_1 [AS nazwa_1] [, [nazwa_tabeli.]pole_2 [AS nazwa_2] [,...]]}
FROM nazwa_tabeli [AS alias] [IN baza_danych]
[WHERE warunek]
[GROUP BY lista_pól]
[HAVING wyrażenie]]
[ORDER BY kryterium1 [,kryterium2 ...]

Uruchomienie edytora SQL i edycja kwerendy w MS Access 97:

Główne tabele wykorzystane w przykładach:

Personel(ID, Imię, Nazwisko, Urodzony, Płeć, Zatrudniony, Dział, Stanowisko, Dane żony/męża, Zamieszkały)

Pracownik(NUMER, NAZWISKO, ETAT, SZEF, PRACUJE_OD, PRACA_POD, PRACA_DOD, ID_ZESP)

Widok Projekt tabeli Personel:

Przykład1 (Boratyn): Wybór z tabeli Personel pól Imię, Nazwisko oraz obliczenie wieku z pola Urodzony (wyrazenie)

Kwerenda :

SELECT Personel.Imię, Personel.Nazwisko, Year(Date())-Year([urodzony]) AS Wiek
FROM Personel;

Wynik:

Przykład2 (Boratyn). Zadanie j.w. z zadeklarowaniem nazw kolumn wynikowych - wykorzystanie AS

SELECT [Imię] AS [First Name (Imię)], [Nazwisko] AS [Last Name - Nazwisko], year(Date())-Year([urodzony]) AS Wiek
FROM Personel AS [Staff - Personel];

Wynik:

Widok w trybie projektowym:

Po przełączeniu okna w tryb projektu widać, że tabela Personel występuje tam pod przybraną nazwą Staff - Personel, która stanowi wartość atrybutu Alias.

O ile nadawanie nazw kolumnom wynikowym jest stosowane powszechnie, o tyle zmiana nazwy tabeli źródłowej przydaje się, gdy korzystamy z tabeli wielokrotnie, traktując ją za każdym razem jako inną tabelę.

Wykorzystanie aliasów ilustruje następny przykład, oparty na tabeli Cyfry dwójkowe (Cyfra), której wiersze zbudowane są tylko z jednego pola. Pole to ma w pierwszym wierszu wartość 0, a w drugim 1. Dzięki 4-krotnemu odwołaniu się do pola tej samej tabeli, otrzymujemy zestawienie wszystkich 4-cyfrowych liczb dwójkowych.

Przykład 3 (Boratyn). Zestawienie czterocyfrowych liczb dwójkowych - wykorzystanie aliasów.

Tabela:

Kwerenda:

Wynik:

Klauzula WHERE

Klauzula WHERE - umożliwia operację selekcji. Po WHERE następuje warunek w postaci wyrazenia. W zestawieniu znajdą się wartości tylko z tych wierszy, dla których wyrazenie będzie spełnione.

Ogólny format polecenia SELECT z klauzulą WHERE:

select atrybuty_projekcji
from relacja
where warunki_do_spełnienia

Przykład 4 (Boratyn). Informacja o wieku pań pracujących w firmie

SELECT [Imię], [Nazwisko], Year(Date())-Year([Urodzony]) AS Wiek
FROM Personel
WHERE Right([Imię],1)="a";

Jako warunku WHERE użyto wyrazenia wybierającego z tabeli źródłowej wiersze, dla których wartość w polu Imię kończy się na literę a. Nie zadziała to dobrze w przypadku mężczyzn o imieniu kończącym się na 'a', np. Barnaba:

Aby uniknąć niespodzianek, dodano pole 'Płeć' i wtedy zapytanie ma postać:

SELECT [Imię], [Nazwisko], Year(Date())-Year([Urodzony]) AS Wiek
FROM Personel
WHERE [Płeć] = 'K';

W najprostszym przypadku po słowie kluczowym where umieszcza się atrybut relacji, operator oraz nazwę atrybutu, literał lub listę literałów. Tak skonstruowany warunek nazywamy warunkiem prostym.

Operatory

Operator Funkcja Przykład
= równość select Nazwisko, Imię, Dział from Personel
where stanowisko='Kierownik';
<> lub != różność SELECT nazwisko, imię from personel
where nazwisko <> 'Dąbek';

(Osoby o nazwisku nie Dąbek)
> większość SELECT nazwisko & " " & Imię AS [Nazwisko i imię], Urodzony AS [Data urodzenia]
FROM Personel
WHERE year(urodzony) > 1970;
>= większość lub równość SELECT Nazwisko, Imię
from Personel
where nazwisko >= 'K%';
< mniejszość SELECT nazwisko & " " & Imię AS [Nazwisko i imię], Urodzony AS [Data urodzenia]
FROM Personel
WHERE year(urodzony) < 1970;
<= mniejszość lub równość select nazwiko, placa_pod, placa_dod from pracownik
where placa_pod <= 2*placa_dod;
is null wartość pusta SELECT [nazwisko] & " " & [Imię] AS [Nazwisko i imię]
FROM Personel
WHERE [Dane żony/męża] is null;
between wyr1 and between wyr2 wartość należy do przedziału
domkniętego
SELECT [ID Pracownika], Personel.Nazwisko, Personel.Imię, [Zasadnicza]
from Płace, Personel WHERE Płace.[ID Pracownika]=Personel.[ID]
and Zasadnicza BETWEEN 800 and 1500;

SELECT p.[ID Pracownika], o.Nazwisko, o.Imię, p.[Zasadnicza]
from Płace as p, Personel as o
where p.[ID Pracownika]=o.[ID]
and Zasadnicza between 800 and 1500;

in (lista) wartość jest jednym z elemantów
listy
SELECT [ID Pracownika], Personel.Nazwisko, Personel.Imię, [Zasadnicza] FROM Płace, Personel
WHERE Płace.[ID Pracownika]=Personel.[ID]
and Płace.Zasadnicza in (700, 1200, 1400, 1500);
like wzornik wartość zgodna z podanym
wzorcem
SELECT [ID Pracownika], Personel.Nazwisko, Personel.Imię, [Zasadnicza]
FROM Płace, Personel
WHERE Płace.[ID Pracownika]=Personel.[ID]
and Personel.Nazwisko like 'Barska';

SELECT [ID Pracownika], Personel.Nazwisko, Personel.Imię, [Zasadnicza]
FROM Płace, Personel
WHERE Płace.[ID Pracownika]=Personel.[ID]
and Personel.Nazwisko like 'B*';

SELECT Personel.Nazwisko, Personel.Imię, Płace.Zasadnicza, Personel.ID
FROM Personel INNER JOIN Płace ON Personel.ID = Płace.[ID Pracownika]
WHERE (((Personel.Nazwisko) Like 'B*') AND ((Personel.ID)=[ID Pracownika]));

is not null wartość niepusta SELECT Nazwisko, Imię
from Personel
where [Dane żony/męża] is not null;
not between and wartość spoza przedziału SELECT [ID Pracownika], Personel.Nazwisko, Personel.Imię, [Zasadnicza]
from Płace, Personel WHERE Płace.[ID Pracownika]=Personel.[ID]
and Zasadnicza not BETWEEN 800 and 1500;
not in (lista) wartość różna od każdego z elementów listy SELECT nazwisko, imię from Personel
where nazwisko not in ('Barska','[Śliwa]')
;
not like wartość niezgodna z wzorcem SELECT nazwisko, imię
FROM personel
WHERE nazwisko not like "D*";

Operator selekcji określony w klauzuli WHERE może być warunkiem złożony, do którego użyto warunków prostych połączonych jednym z 2 operatorów logicznych: AND (koniunkcja) i OR (alternatywa). AND ma wyższy priorytet niż OR.

Kolejność wartościowania operatorów:

  1. Relacyjne i specjalne o jednakowym, najwyższym priorytecie: =, !=, <, >, <=, >=, between .. and .., in, like, is null
  2. Ligiczny NOT
  3. Logiczny AND
  4. Logiczny OR

Przykłady:

SELECT nazwisko, etat, placa_pod from pracownik where etat='ADIUNKT' or placa_pod between 1000 and 2000;

W poleceniu tym szukamy wszystkich adiunktów oraz pracowników na innych etatach, o zarobkach z przedziału od 1000 do 2000 zł.

Select * from pracownik where placa_pod > 1500 and (etat='ADIUNKT' or etat = 'ASYSTENT');

W poleceniu odszukujemy wszystkich adiunktów i asystentów o zarobkach większych od 1500 zł.

Klauzula ORDER BY

Klauzula order by, która może wystąpić tylko jako ostatnia klauzula polecenia select, powoduje posortowanie wierszy będących wynikiem zapytania wg wartości atrybutu w niej wskazanego. Domyślnie przyjmuje się rosnący porządek sortowania (asc), może być odwrotne (desc). Sortowanie może odbywać się na podstawie wielu atrybutów (liczba ograniczona liczbą atrybutów relacji). Kolejność atrybutów w klauzuli order by jest istotna: porządkowanie odbywa się najpierw wg wartosci pierwszego atrybutu, następnie, jeśli wartości te są równe dla 2 lub więcej krotek, wg drugiego atrybutu itd. Użycie atrybutu w klauzuli order by nie pociąga za sobą konieczności jego użycia w klauzuli select.

Przykład: Wyświetlić wszystkie informacje o pracownikach z tabeli Pracownik, z posortowaniem najpierw wg numeru zespołu (id_zesp), a nastepnie, w ramach każdego zespołu wg płacy podstawowej, malejąco.
SELECT *
from pracownik
order by id_zesp, placa_pod desc;

Przykład 5. Wyświetlić: Imię, Nazwisko, Wiek z tabeli Personel. Wiek posortować malejąco a następnie Nazwisko rosnąco.

SELECT personel.imię, nazwisko, year(date())-year([urodzony]) AS Wiek
FROM Personel
ORDER BY year(date())-year([urodzony]) DESC , [Nazwisko];

PARMETERS

Często są stosowane zapytania z parametrami. Np. parametrem zapytania może być nazwisko. W SQL do deklaracji parametrów służy słowo PARAMETERS, stosowane wyłącznie na początku całego wyrażenia, przed SELECT. Bezpośrednio po PARMETERS znajdują sie nazwy parametrów (w nawiasach prostokątnych, gdy zawierają spacje lub znaki specjalne, np. polskie), wraz z typem danych (np. TEXT).

Przykład 5. Wyświetlić wszystkie dane pracownika o podanym nazwisku, z tabeli Personel

PARAMETERS [Podaj nazwisko:] Text;
SELECT *
FROM Personel
WHERE [Nazwisko]=[Podaj nazwisko:];

Jeśli będzie więcej pracowników o tym samym nazwisku, to zostaną wszyscy wyświetleni. Aby uniknąć wyboru danych dotyczących wszystkich pracowników noszących to samo nazwisko, należy zadeklarować drugi parametr, np. Imię.

PARAMETERS [Podaj nazwisko:] Text, [Podaj imię:] Text;
SELECT *
FROM Personel
WHERE [Nazwisko]=[Podaj nazwisko:]
AND [Imię]=[Podaj imię:];

Predykaty ALL, DISTINCT, DISTINCTROW, TOP

Aby ograniczyć się do pojedynczych wystąpień identycznych wierszy lub o jednakowych wartościach w niektórych polach, używa się predykatów (słów kluczowych). Predykaty ograniczają występowanie takich samych lub podobnych wierszy w zestawieniu.

Zadanie: wykaz miejscowości, poza miejscem lokalizacji firmy (z których np. muszą dojeżdżać pracownicy zamieszkali poza siedzibą).

Przykład8

SELECT [Zamieszkały]
FROM Personel
WHERE [Zamieszkały] <> "Wrocław";

Zapytanie ma tę wadę, że wybierze wszystkie nazwy z pola Zamieszkały, oprócz Wrocławia, a więc i powtarzające się. Aby ograniczyć się do pojedynczych wystąpień wszystkich nazwa, należy po słowie SELECT użyć predykatu DISTINCT. Odpowiada on atrybutowi wartości unikatowe w trybie QBE. Jego zadanie to eliminacja z zapytania tych wierszy, w których choć jedno pole ma tę samą wartość co w wierszu wcześniejszym. Zatem zapytanie wybierające tylko wartosci różniące się od siebie w nawiązaniu do powyższego przykładu, powinno mieć postać (Przykład9):

SELECT DISTINCT [Zamieszkały]
FROM Personel
WHERE [Zamieszkały] <> "Wrocław";

Inne predykaty to ALL, DISTINCTROW i TOP.

ALL jest stosowany domyślnie, gdy nie używa się innego. Predykat DISTINCTROW stosuje się wyłącznie w zapytaniu wybierającym dane z kilku tabel, dzięki czemu w zestawieniu nie będzie identycznych wierszy. Odpowiada on atrybutowi Rekordy unikatowe w QBE. Predykat TOP n (gdzie n jest liczbą naturalną) pozwala wybrać nie wszystkie, ale n pierwszych wierszy spełniających warunki zapytania. Ma on swój odpowiednik w postaci atrybutu zapytań Najwyższe wartości.

W przedstawionym poniżej przykładzie (Przykład10), zapytanie wybiera 3 pracowników o najdłuższym stażu pracy w firmie.

SELECT TOP 3 *
FROM Personel
ORDER BY [Zatrudniony od];


Funkcje grupowe

Klauzule GROUP BY, Having

Klauzula GROUP BY umożliwia podział krotek (rekordów, wierszy) relacji na grupy. Krotki tej samej grupy mają identyczną wartość atrybutu grupowania, który wskazano w klauzuli. Po podziale, do każdej z grup można zastosować jedną z tzw. funkcji grupowych/agregujących ( np. liczącej wiersze w grupie - count, liczącej wartość średnią w grupie wierszy dla danego pola), w szczególności funkcję count, umożliwiającą określenie liczebności grupy. Klauzula group by moze być stosowana rekurencyjnie, co oznacza, że można w niej wskazać wiele atrybutów grupowania. W takim przypadku możliwe jest wydzielenie podgrup w ramch wcześniej wydzielonych grup.
Jeżeli w klauzuli group by użyjemy nazw kilku kolumn otrzymamy 'grupy wewnątrz grup'. Kolejność grupowania od lewej do prawej.

Przed pogrupowaniem wierszy odbywa się ich selekcja na podstawie warunku WHERE, o ile został on w zapytaniu użyty.

Aby wyeliminować wiersze po podziale na grupy, należy dodatkowo użyć warunku HAVING.
Można dodatkowo używać klauzuli HAVING wyrażenie logiczne.
Spowoduje to wybranie tylko tych grup, które spełniają warunek z having.

Stosowanie funkcji grupowych w klauzuli select wyklucza możliwość wyświetlania atrybutów pojedynczych krotek, chyba że nazwa atrybutu jest wymieniona w klauzuli group by. W konsekwencji, jeżeli w klauzuli select użyto wraz z funkcjami grupowymi n atrybutów, to atrybuty te muszą się znaleźć w klauzuli group by.

Na liście wyboru komendy select używającej funkcji grupowych wolno umieszczać tylko te kolumny, które występują w klauzuli group by (oprócz samych funkcji grupowych).

Przykłady:

1) Pogrupowanie krotek z relacji Pracownik wg atrybutu id_zesp oraz zliczenie krotek w ramach grupy - wyświetlenie liczby pracowników w ramach każdego zespołu:

SELECT id_zesp as [Zespół], count(*) as [Ilość osób w zespole]
from pracownik
group by id_zesp;

2) Określenie liczebności różnych grup etatowych, z pominieciem dyrektorów, w ramach wcześniej wydzielonych grup zespołowych - z relacji Pracownik

SELECT id_zesp, etat, count(*) AS [Ilość grup etatowych w zespołach]
FROM pracownik
WHERE etat <> 'DYREKTOR'
GROUP BY id_zesp, etat;

Najpierw z relacji Pracownik są odrzucane krotki reprezentujące dyrektorów (selekcja). W drugim kroku, pozostałe krotki są grupowane wg wartości atrybutu id_zesp. Nastepnie każda z grup jest dzielona na podgrupy wg wartości atrybutu etat. Na zakończenie, dla każdej podgrupy jest wykonywana funkcja count.

Klauzula HAVING

Klauzula HAVING, podobnie jak WHERE, umożliwia selekcję informacji. W odróżnieniu jednak od klauzuli where, która operuje na pojedynczych krotkach relacji, klauzula having operuje na wcześniej wydzielonych grupach, a więc na wyniku działania klauzuli group by.

Z logicznego punktu widzenia, klauzula having powinna być stosowana po klauzuli group by. Tworzenie grup i obliczanie funkcji grupowych jest bowiem realizowane przed selekcją grup.

Przykład: Wyświetlić identyfikatory i średnie płace podstawowe pracowników zespołów o liczebności większej od 3, na podstawie tabeli Pracownik - bez formatowania wydruku i z formatowaniem płacy w postaci walutowej (funkcja CCur w Accessie).

SELECT id_zesp, avg(placa_pod)
FROM pracownik
GROUP BY id_zesp
HAVING count(*) >3;

SELECT id_zesp, CCur(avg(placa_pod) )
FROM pracownik
GROUP BY id_zesp
HAVING count(*) >3;

W poleceniu najpierw dzieli się pracowników (z tabeli Pracownik) na grupy wg identyfikatora zespołu id_zesp. Następnie dla każdej z grup obliczana jest średnia płaca podstawowa (funkcja avg) i liczebność tej grupu (count). Na koniec selekcjonowane są grupy spełniające warunek klauzuli having, a zatem zespoły o liczebności większej od trzech.

Przykład. Wynik zapytania j.w. - ale zapytanie dotyczy zespołów 20 i 30
SELECT id_zesp, CCur(avg(placa_pod) ) AS [Średnia płaca podst]
FROM pracownik
WHERE id_zesp in (20, 30)
GROUP BY id_zesp;

Przykład: Zapytanie wyświetlające ilość adiunktów
SELECT count (*) as 'Liczba adiunktów' from Pracownik where etat='ADIUNKT';

Przykład: Płace średnie (podstawowa + dodatkowa) wg zespołów i etatów

SELECT id_zesp, etat, ccur(avg(placa_pod+placa_dod)) as [Płaca średnia]
from Pracownik
group by id_zesp, etat;

Przykład: Średnie zarobki (płaca podst.) na każdym stanowisku oprócz dyrektora

SELECT etat, avg(placa_pod)
from Pracownik
where etat <> 'DYREKTOR'
group by etat;

Przykład11. Podanie parametru dotyczącego płci (K lub M) i wyświetlenia ilości zatrudnionych osób danej płci w posczególnych działach.

PARAMETERS [(K)obieta czy (M)ężczyzna?] Text;
SELECT [Dział], [Płeć], Count([Nazwisko]) AS [Ilość zatrudnionych]
FROM Personel
GROUP BY [Dział], [Płeć]
HAVING [Płeć]=[(K)obieta czy (M)ężczyzna?]
ORDER BY [Dział];

Po uruchomieniu zapytanie dzieli wiersze tabeli Personel na grupy wg pola Dział. Następnie w ramach każdego działu tworzone są podgrupy, osobno dla danych kobiet, osobno dla mężczyzn. W każdej z takich grup liczone są wiersze, a wyświetlany jest rezultat tylko dla płci zgodnej z podanym parametrem.

Przykład11a. Wyświetlenie ilości zatrudnionych osób w działach wg płci, sortowanie wg płci malejące (DESC)

SELECT [Dział], [Płeć], Count([Nazwisko]) AS [Ilość zatrudnionych]
FROM Personel
GROUP BY [Dział], [Płeć]
ORDER BY [Dział], [Płeć] DESC;

Funkcje grupowe

Funkcja Opis  
AVG([distinct|all]atr) wartość średnia wartości atrybutu wszystkich krotek grupy  
COUNT([distinct|all]wyr) liczba wierszy, dla których wyrażenie wyr ma wartość różną od null  
COUNT([ALL]*) ilość wierszy zwracana przez zapytanie lub podzapytanie  
MAX([distinct|all]wyr) maksymalna wartość wyrazenia wyr  
MIN([distinct|all]wyr) minimalna wartośc wyr  
STDEV lub STDDEV([distinct|all]atr) odchylenie standardowe wartości atrybutu atr  
VAR([distinct|all]atr) lub VARIANCE wariancja wartości atrybutu  
SUM([distinct|all]atr) suma wartości atrybutu atr  

Łączenie relacji - połączenia w SQL

W przypadku gdy realizacja zapytania wymaga dostępu do więcej niż jednej relacji, istnieje możliwość połączenia tych relacji z opcjonalnym wykonaniem na nich innych operacji algebry relacji (np. projekcji - podzbiór pionowy - wybrane atrybuty lub selekcji - podzbiór poziomy).

Poziome łączenie relacji polega na utworzeniu relacji wynikowej, której krotki są wynikiem konkatenacji wybranych krotek relacji źródłowych. Pionowe łączenie relacji polega na utworzeniu relacji wynikowej, której krotki są sumą, częścią wspólną lub różnicą krotek relacji źródowych.

Poziome łączenie relacji

Najprostszym sposobem poziomego łączenia relacji jest zastosowanie operatora produktu kartezjańskiego, w wyniku której powstaje relacja, której krotki są konkatenacją wszystkich krotek relacji źródłowych (tj. wszystkich możliwych kombinacji).

Z syntaktycznego punktu widzenia łączone relacje wymienia się kolejno w klauzuli from polecenia select, oddzielając je przecinkami.

Przykład
select * from pracownik, zespol;

W praktyce takie łączenie jest stosowane rzadko, choćby ze względu na duży rozmiar relacji wynikowej, której liczba krotek w powyższym przykładzie jest iloczynem liczby krotek relacji Pracownik i Zespol. Ponadto długość krotek (rekordów) wynikowych jest równa sumie długości krotek ralacji źródłowych (tu Pracownik i Zespol).

Znacznie częściej stosuje się tzw. połączenie - join. W tym przypadku krotki jednej relacji są łączone z krotkami innej relacji tylko wtedy, gdy wartości korespondujących atrybutów tych krotek spełniają określony warunek, nazywany warunkiem połączenia, umieszczany w klauzuli where.

select atrybut(y) from łączone_relacje where warunek_połączenia

W przypadku gdy łączone relacje mają atrybuty o tych samych nazwach powstaje niejednoznaczność. W celu jej usunięcia, nazwy atrybutów poprzedzane są nazwami relacji. Takie prefiksowanie stosujemy także w klauzulach select, group by i order by. Jeżeli trzeba wielokrotnie prefiksować nazwy relacji, w zamian można zastosować aliasy, definiowane w klauzuli from.

Przykład. Wyświetlenie nazwiska pracownika z tabeli Pracownik i nazwy zespołu (nazwa) z tabeli Zespol - określenie w jakim zespole pracują poszczególni pracownicy. W tym celu pobieramy wartość atrybutu id_zesp krotki każdego pracownika a następnie odszukujemy taką krotkę relacji zespol, w której wartość atrybutu id_zesp jest równa wartości pobranej. Dopasowane krotki łączymy ze sobą i po wykonaniu projekcji (podzbiór pionowy) atrybutów nazwisko i nazwa, umieszczamy w relacji wynikowej.

a) Bez aliasów - atrybuty poprzedzone nazwami ralacji (tabel)

SELECT pracownik.nazwisko, zespol.nazwa
from Pracownik, Zespol
where Pracownik.id_zesp=zespol.id_zesp;

b) z aliasami p (pracownik) i z (zespol)

SELECT nazwisko, nazwa
FROM Pracownik p, Zespol z
WHERE p.id_zesp=z.id_zesp;

c) z zastosowaniem JOIN

SELECT pracownik.nazwisko, zespol.nazwa
FROM Pracownik
INNER JOIN zespol
on pracownik.id_zesp=zespol.id_zesp;

d) Odmiana przykładu JOIN ze słowem kluczowym RIGHT

SELECT pracownik.nazwisko, zespol.nazwa
FROM Pracownik
RIHT JOIN zespol
on pracownik.id_zesp=zespol.id_zesp;

Przykład. Wyświetlić także zespoły, w których nikt nie pracuje

SELECT nazwisko, nazwa, z.id_zesp
FROM Pracownik AS p RIGHT JOIN zespol AS z ON p.id_zesp = z.id_zesp;

Zastosowano tu słowa RIGHT JOIN, dzięki czemu został uwzględniony zespół 50, który nie zatrudnia pracowników

Ogólny schemat zapytania wybierającego uwzględniającego różne tabele źródłowe i rodzaje połączenia

SELECT atrybut(y)
FROM
tabela1 {INNER | LEFT | RIGHT} JOIN (tabela2
{INNER | LEFT | RIGHT } JOIN [(tabela3
{INNER | LEFT | RIGHT } JOIN [(tabelax
{INNER | LEFT | RIGHT } JOIN ...)]
ON tabela3.pole3=tabelax.polex)]
ON tabela2.pole2=tabela3.pole3)
ON tabela1.pole1=tabela2.pole2;

Skomplikowana struktura tego wyrażenia wynika z faktu, że uwzględnia ono możliwość powiązania i wykorzystania w zapytaniu wielu tabel źródłowych. Jego podstawą jest jednak połączenie 2 tabel, które przedstawia się następujaco:

tabela1 {INNER | LEFT | RIGHT} JOIN tabela2 ON tabela1.pole1 = tabela2.pole2

Oprócz wskazania tabel, definicja połączenia musi zawierać nazwy odpowiadających sobie pól z każdej tabeli.

Występujące przy słowie kluczowym JOIN przymiotniki odpowiadają różnym rodzajom połączeń. INNER JOIN to tzw. połączenie zawężające, LEFT/RIGHT JOIN to odpowiednio połączenie lewe lub prawe, tworzace klasę połączeń rozszerzających.

Przykład12. Zapytanie tworzące listę nazwisk pracowników i nazw kursów, które mogą poprowadzić. Tabela Wykładowcy zawiera ID Pracownika i ID Kursu. Nazwiska pochodzić będą z tabeli Personel, a nazwa kursu z tabeli Oferta. Takie zestawienie wymaga 3 tabel źródłowych i wybrania tylko tych wierszy, dla których wartości odpowiadających sobie pól są równe. Zapytanie to wybierze z tabeli Personel nazwiska tylko tych pracowników, których identyfikator figuruje w tabeli Wykładowcy - połączenie INNER JOIN

SELECT Personel.Nazwisko, Oferta.[Nazwa kursu]
FROM Personel INNER JOIN (Wykładowcy INNER JOIN Oferta ON Wykładowcy.[ID Kursu] = Oferta.[ID kursu]) ON Personel.ID = Wykładowcy.[ID Pracownika]
ORDER BY Personel.Nazwisko;

Przykład13. Przykład połączenia rozszerzającego, które wybiera wszystkie wiersze z jednej tabeli i tylko te wiersze z drugiej tabeli, dla których wartości odpowiadających sobie pól z obydwu tabel są równe. Probem absencji pracy - niektórzy mają często zwolnienia lekarskie, inni nie opuścili żadnego dnia. Dane na ten temat zapisywane w tabeli Zwolnienia lekarskie(ID Pracownika, Ilość dni). Okresowy raport ma zawierać listę nazwisk z łączną liczbą dni na zwolnieniu. Oprócz połączenia tabel Personel i Zwolnienia lekarskie zapytanie zawiera operator GROUP BY. Ponieważ zapytanie zawiera także nazwiska osób, które ze zwolnienia nie korzystały, zastosowano połączenie rozszerzające, dzięki czemu wybrane zostaną wszystkie nazwiska z tabeli Personel, a liczba nieobecności będzie policzona tylko dla tych, którzy je posiadają. Połączenie LEFT JOIN bo po lewej stronie wzięto tabelę Peronel (wszyscy pracownicy). Funkcji Format użyto po to, by przy nazwisku osób bez nieobecności wyświetlone zostało 0 zamisat pustego miejsca.

SELECT [Personel].[Nazwisko], Personel.ID, Format(Sum([Zwolnienia lekarskie].[Ilość dni]),"0;0;0;0") AS [Absencja łącznie]
FROM Personel LEFT JOIN [Zwolnienia lekarskie] ON Personel.ID = [Zwolnienia lekarskie].[ID Pracownika]
GROUP BY [Personel].[Nazwisko], [Personel].[ID]
ORDER BY Sum([Zwolnienia lekarskie].[Ilość dni]) DESC , Personel.Nazwisko;


Zapytania zagnieżdżone - podzapytania

Polecenia select mogą być w sobie zagnieżdżane, co umożliwia realizację bardziej zaawansowanych operacji na relacjach, w szczególności dzięki możliwości tworzenia klauzul selekcji where, których postać jest zależna od wyniku(ów) zapytania zagnieżdżonego. Zapytanie zagnieżdżone jest zwykle nazywane podzapytaniem (subquery) lub zapytaniem wewnętrznym, w odróżnieniu od zapytania zewnętrznego.

Zapytania zagnieżdżone to zwykłe zapytania wybierające zbudowane na bazie SELECT ... FROM, których definicja została wpisana w definicji dowolnego innego zapytania, także zapytania zagnieżdżonego - zapytanie w zapytaniu. Zapytanie zagnieżdżone może pojawić się w zapytaniu głównym w 2 miejscach: w wyrażeniu po słowie SELECT oraz w warunku WHERE i/lub HAVING dla zapytania zbiorczego. W Accessie, SQL-owe wyrażenie SELECT, definiujące zapytanie zagnieżdżone, może pojawić się także w szablonie QBE, w rubrykach Pole i/lub Kryteria. W szablonie QBE wyrażenia SQL muszą być ujęte w nawiasy okrągłe!

Przykład15. Struktura zatrudnienia w każdym z działów, w oparciu o tabelę Personel.

SELECT Personel.Dział, Format(Count([Nazwisko])/
(SELECT Count([Nazwisko])
FROM [Personel]),
"0%") AS [Odsetek zatrudnionych]
FROM Personel
GROUP BY Personel.Dział;

Przykład15a. Policzenie stopnia feminizacji każdego z działów firmy, czyli ilorazu pracujących pań do wszystkich zatrudnionych w dziale. Użyto aliasu Personel_1, by ta sama tabela "robiła" za różne.

SELECT Personel_1.Dział, Format(Count([Nazwisko])/(SELECT Count([Nazwisko])
FROM Personel
WHERE
Personel_1.Dział=Personel.Dział),
"0 %") AS Feminizacja
FROM Personel AS Personel_1
WHERE (Personel_1.Płeć)="K"
GROUP BY Personel_1.Dział;

W wyrażeniach określających warunki WHERE i HAVING rozróżnia się 3 konstrukje składniowe, w których można użyć zapytań zagnieżdżonych:

Konstrukcja pierwsza

{WHERE | HAVING}
{Nazwa_pola | Wyrażenie} = {= | < | > | <> } [ALL | ANY | SOME]
(SELECT ... FROM)

Konstrukcja druga

{WHERE | HAVING}
{Nazwa_pola | Wyrażenie} [NOT] IN
(SELECT ... FROM)

Konstrukcja trzecia

{WHERE | HAVING}
[NOT] EXISTS
(SELECT ... FROM)

Przykłady konstrukcji pierwszej

Przykład16. Nazwiska osób, których wiek przekracza średnią dla całej firmy. Ponieważ nie znamy średniej wieku, zamiast liczby w warunku WHERE wstawiamy proste zapytanie, które ją policzy. Zapytanie zagnieżdżone oblicza śrdnią za każdym razem przy sprawdzaniu warunku WHERE, co może wydłużyć czas oczekiwania na rezultat. Konstrukcja WHERE wyra zenie > (SELECT ... FROM)

SELECT [Nazwisko], Year( Date() )-Year( [Urodzony] ) AS Wiek
FROM Personel
WHERE Year ( Date() ) - Year ( [Urodzony] ) >
(SELECT
Avg (Year (Date() ) - Year ( [Urodzony] ) )
FROM Personel);

Przykład17. Nazwiska tych kierowników firmy, którzy są starsi wiekiem od wszystkich swoich podwładnych. Konstrukcja z predykatem ALL i operatorem > (lewa strona nierówności większa od wszystkich wartości stanowiących wynik zapytania zagnieżdżonego). Zapytanie wybierze tylko tych kierowników, których wiek przekracza wiek każdego pracownika.

SELECT [Dział], [Nazwisko], Year( Date() )-Year( [Urodzony] ) AS Wiek
FROM Personel AS Personel_1
WHERE [Stanowisko]="Kierownik" AND
(Year( Date()) - Year(Urodzony) ) > ALL
(SELECT
(Year (Date()) - Year ( Urodzony ) )
FROM Personel
WHERE
[Stanowisko] <> "Kierownik" AND
[Personel].[Dział]=[Personel_1].Dział);

Przykład konstrukcji drugiej

Przykład18. Działy i nazwiska osób bez znajomości języków obcych - konstrukcja NOT IN

SELECT [Dział], [Nazwisko]
FROM Personel
WHERE [ID] NOT IN
(SELECT
[ID Pracownika]
FROM
[Języki obce]);

Zapytanie spełniające te same funkcje, korzystające z połaczeń:

SELECT Personel.Dział, Personel.Nazwisko
FROM Personel LEFT JOIN [Języki obce] ON Personel.[ID]=[Języki obce].[ID Pracownika]
WHERE ( [Języki obce].[Język obcy] IS Null );

Przykład konstrukcji trzeciej (z WHERE EXISTS)

Przykład19. Zapytanie wybierające nazwisko i imię pracownika, dział w którym jest zatrudniony, oraz nazwisko, imię i dział, w którym pracuje jego żona - chodzi o małżeństwa zatrudnione w firmie.

SELECT [Imię] & " " & [Nazwisko] AS [Imię i nazwisko], [Dział], [Dane żony/męża], ( SELECT [Dział]
FROM [Personel]
WHERE
[Personel].[Imię] & " " & [Personel].[Nazwisko] = [Personel_1].[Dane żony/męża] ) AS [Dział małżonka]
FROM Personel AS Personel_1
WHERE EXIST
( SELECT [ID]
FROM [Personel]
WHERE
[Płeć] = "K" AND [Personel].[Dane żony/męża] = [Personel_1].[Imię] & " " & [Personel_1].[Nazwisko] );


Zapytania modyfikujące

Należą do nich m.in. zapytania dołączajace i tworzące tabele. Różnią się one w z zasadzie obiektem docelowym.
Zapytanie dołączajace (append query) wpisuje wybrane z tabel źródłowych wartości jako nowe wiersze istniejącej tabeli.
Zapytanie tworzące tabelę (make table query) buduje dla tych wartości specjalną tabelę.
W obydwu zapytaniach musi wystąpić blok kwalifikacji, czyli konstrukcja SELECT... FROM... WHERE..., wybierający z tabeli (tabel) dane źródłowe, które zostaną dopisane do istniejącej lub nowej tabeli.

Zapytania dołączające (append query)

Ogólna postać zapytania dołączającego:

INSERT INTO nazwa_tabeli_docelowej [(pole1 [, pole2 [,... ]])] [IN nazwa_zewn_bazy_danych]
SELECT [nazwa_tabeli_źródłowej.]pole1 [,pole2 [,...]] FROM nazwa_tabeli_źródłowej;

Lista pól tabeli docelowej jest zbędna, gdy noszą one te same nazwy co pola tabeli źródłowj wymienione po SELECT (można zamiast nazw użyć gwiazdki). Wstawiając zapytaniem nowe wiersze do tabeli docelowej, należy zwrócić uwagę na pole klucza głównego. Nie może być puste a wpisana wartość musi jednoznacznie identyfikować wiersz. Jedynie gdy pole klucza głównego jest typu licznikowego, jego uzupełnienie można zostawić Accessowi (dla programu Access).

Przykład: Dołączenie etatów na literę S z tabeli ETAT do ETAT1. Tabela ETAT1 musi istnieć. Zapytanie zapisać pod odpowiednią nazwą - Dołączające etaty na literę S z tabeli ETAT do tabeli ETAT1

INSERT INTO ETAT1
SELECT *
FROM ETAT
WHERE etat like "s*";

Korzystając z pewnej odmiany zapytania dołączającego, można wpisać w tabelę docelową wartosci stałe, nie pochodzące z żadnej tabeli źródłowej. W definicji takiego zapytania, zamiast bloku SELECT... FROM..., należy wpisać listę wartości poprzedzoną słowem VALUE, wg schematu:

INSERT INTO nazwa_tabeli_docelowej [(polw1 [,pole2 [,...]])] [IN nazwa_zewn_bazy_danych]
VALUES ("wartosć1" [, "wartosć2" [,...]]);

Przykład:

INSERT INTO ETAT1
VALUES ("Pracownik administr", "900", "1100");

Ważne jest, by kolejność wyliczonych wartości odpowiadała kolejnosci pól tabeli docelowej oraz by poszczególne wartosci ująć w cudzysłowy i rozdzielić przecinkami. Dopuszczalne jest także korzystanie z wyrażeń i parametrów. Wtedy cudzysłowy nie są potrzebne.

Zapytania tworzące tabelę (make table query)

Zapytanie tworzące tabelę składa się z bloku SELECT... FROM..., w który wstawiono dodatkowo słowo INTO:
SELECT pole1 [, pole2 [,...]] INTO nazwa_nowej_tabeli [IN nazwa_zewn_bazy_danych]
FROM nazwa_tabeli_źródłowej;

Pola nowej tabeli dziedziczą po "przodkach" jedynie typ danych i rozmiar pola. Pozostałe atrybuty mają swe ustawienia standardowe, w sczególności stworzona zapytaniem tabela nie ma klucza głównego ani żadnego ineksu.

Przykłady.

a) Zapytanie tworzące tabelę ETAT2 na podstawie tabeli ETAT, dołączające wszystkie pola i wszystkie rekordy

SELECT *
INTO ETAT2
FROM ETAT;

b) Tworzące tabelę ETAT3 na podstawie tabeli ETAT, tworzy tylko 2 podane pola (ETAT i PLACA_MIN)

SELECT ETAT, PLACA_MIN INTO ETAT3
FROM ETAT;

c) J.w. z warunkiem placa_MAX >= 1600

SELECT ETAT, PLACA_MIN
INTO ETAT4
FROM ETAT
WHERE placa_max >=1600;

Przykłady z książki Boratyna: Zapytanie archiwizujące wybrane dane z tabeli [Przebieg szkoleń] - wiersze odnoszące się do kursów zakończonych w poprzednim miesiącu. Zadanie to można zrealizować na 2 sposoby: tworząc osobną tabelę dla każdego miesiąca (zapytanie tworzące tabelę) lub dołączając dane do tabeli zbiorczej (zapytanie dołącające).

Przykład20. Zapytanie tworzące nową tabelę Archiwum. Zapytanie usuwa poprzednią, istniejącą tabelę Archiwum, dlatego należy zmienić jej nazwę na inną.

SELECT [Szkolenie nr], [Temat], [Prowadzący], [Początek], [Koniec], [Ilość osób]
INTO Archiwum
FROM [Przebieg szkoleń]
WHERE Month([Koniec])=Month(Date())-1;

Przykład21a. Zapytanie dołączające - dołącza do istniejącej tabeli Archiwum rekordy spełniające podany warunek (miesiąc o jeden mniejszy od aktualnego).

INSERT INTO Archiwum ( [Szkolenie nr], Temat, [Prowadzący], [Początek], Koniec, [Ilość osób] )
SELECT [Szkolenie nr], [Temat], [Prowadzący], [Początek], [Koniec], [Ilość osób]
FROM [Przebieg szkoleń]
WHERE Month([Koniec])=Month(Date())-1;

Zapytania usuwające

Zapytanie usuwające ma za zadanie usunąć z tabeli źródłowej wszystkie wiersze spełniające podane kryterium. Upraszczając, można powiedzieć, że SQL-owy zapis zapytania usuwającego różni się od zapisu zapytania wybierającego jedynie słowem DELETE, które zastępuje słowo SELECT. Składnia zapytania:

DELETE [nazwa_tabeli_źródłowej.*]
FROM nazwa_tabeli_źródłowej
[WHERE kryterium];2

Przykład:

DELETE (lub DELETE *)
FROM Personel2;

Polecenie DELETE zastepuje nie tylko słowo SELECT, ale również listę pól wybieranych do zestawienia. Zapytanie usuwająe operuje bowiem na całych wierszach tabeli źródłowej, a nie na poszczególnych polach jak zapytanie wybierające.

Przykład22. Usunąć z tabeli Przebieg szkoleń rekordy odnoszące się do kursów zakonczonych w poprzednim miesiącu.

DELETE *
FROM [Przebieg szkoleń]
WHERE Month([Koniec]) = Month(Date())-1;

Przykład23. Zapytanie zbudowane na 2 tabelach będących w związku jeden (Personel) do wielu (Języki obce). Zapytanie to usuwa wiersze wyłącznie z tabeli pozostającej po stronie wiele tego związku. W tym przypadku z tabeli Języki obce usuwane są wszystkie wiersze dotyczące pracownika o podanym nazwisku. Jest to zapytanie z parametrem, jednak deklaracja PARAMETERS nie jest tu konieczna.

DELETE [Języki obce].*
FROM [Języki obce]
INNER JOIN Personel
ON [Języki obce].[ID Pracownika] = [Personel].[ID]
WHERE [Personel].[Nazwisko] = [Podaj nazwisko:];

Uwaga! Działanie zapytania usuwającego jest nieodwracalne!

 

Zapytania aktualizujące (update query)

Za pomocą zapytania aktualizującego można zmieniać wartości poszczególnych pól w wielu wierszach tabeli źródłowej. Przykładem może byc zmiana ceny wszystkich oferowanych szkoleń, np. obniżka wakacyjna o 25%, zmiana nazwy działu Administacja na Management, zmiana stawki o 15%.

Ogólna postać zapytania aktualizującego:

UPDATE nazwa_tabeli_źródłowej
SET wyrażenie1 [, wyrażenie2 [,...]]
[WHERE kryterium];

Inny zapis:

UPDATE relacja [alias]
SET atrybut [, atrybut] = {wyrażenie|podzapytanie}
WHERE warunek;

Przykład: Modyfikacja pola PLACA_DOD w tabeli Pracownik dla pola Numer=1050

UPDATE Pracownik
SET PLACA_DOD = 0
WHERE Numer=1050;

Możliwa jest także aktualizacja pól w różnych tabelach, gdy zamiast pojedynczej tabeli źródłowej użyjemy połączenia 2 i wiecej tabel. W warunku WHERE możliwe jest zagnieżdżenie zapytania.

Przykład24 (Boratyn). Zapytanie aktualizujące, które zmienia o 15% wartość płacy zasadniczej pracowników, którzy znają więcej niż jeden język obcy.

UPDATE [Płace]
SET [Płace].Zasadnicza = [Zasadnicza]*1.15
WHERE
(SELECT Count([Język obcy])
FROM [Języki obce]
GROUP BY [ID Pracownika]
HAVING [Języki obce].[ID Pracownika]=[Płace].[ID Pracownika])
>1;

Modyfikacja struktury danych - ALTER TABLE

Modyfikuje tabelę uprzednio utworzoną za pomocą instrukcji CREATE TABLE.
Składnia
ALTER TABLE tabela {ADD {COLUMN typ_pola[(rozmiar)] [NOT NULL] [CONSTRAINT indeks] |
CONSTRAINT indeks_wielopolowy} |
DROP {COLUMN pole I CONSTRAINT nazwa_indeksu} }
Składnia instrukcji ALTER TABLE zawiera następujące elementy:
tabela - Nazwa tabeli mającej ulec zmianie.
pole - Nazwa pola, które ma zostać dodane lub usunięte z tabeli.
typ - Typ danych pola.
rozmiar - Rozmiar pola w znakach (tylko dla pól typu Text i Binary).
indeks - Indeks pola.

Za pomocą instrukcji ALTER TABLE można zmienić istniejącą tabelę na kilka sposobów:
· Klauzula ADD COLUMN powoduje dodanie nowego pola do tabeli. Należy określić nazwę pola, typ danych oraz opcjonalnie rozmiar (dla pół tekstowych i binarnych). Na przykład, następująca instrukcja dodaje pole tekstowe o długości 25 znaków i nazwie Uwagi do tabeli Pracownicy:
ALTER TABLE Pracownicy ADD COLUMN Uwagi TEXT(25)
Jeśli na pole nałożono ograniczenie NOT NULL, to nowe rekordy muszą zawierać w tym polu prawidłowe dane.
· Klauzula ADD CONSTRAINT powoduje dodanie indeksu wielopolowego. Więcej informacji o indeksach wielopolowych można znaleźć pod hasłem CONSTRAINT.
· Klauzula DROP COLUMN powoduje usunięcie pola. W tym celu należy określić jedynie nazwę pola.
· Klauzula DROP CONSTRAINT powoduje usunięcie indeksu wielopolowego. W tym celu należy po słowie zastrzeżonym CONSTRAINT określić jedynie nazwę indeksu.
Uwagi
· Nie można jednocześnie dodać lub usunąć więcej niż jednego pola lub indeksu.
· W celu dodania do tabeli indeksu jedno- lub wielopolowego, należy użyć instrukcji CREATE INDEX, zaś w celu usunięcia indeksu utworzonego za pomocą instrukcji ALTER TABLE lub CREATE INDEX należy użyć instrukcji ALTER TABLE lub DROP.
· Ograniczenie NOT NULL może być nałożone na pojedyncze pole lub też wystąpić w nazwanej klauzuli CONSTRAINT dotyczącej pojedynczego pola lub też wielu pól. Ograniczenie NOT NULL może być jednak nałożone tylko raz w odniesieniu do jednego pola, gdyż w przeciwnym przypadku wystąpi błąd wykonania.

Dodanie atrybutu do relacji: ALTER TABLE tabela ADD (kolumna typ)

Przykład: Dodanie atrybutu Salary typu walutowy, w tabeli Etat4

ALTER TABLE Etat4 ADD COLUMN Salary CURRENCY;

Usuwanie atrybutu relacji: ALTER TABLE DROP COLUMN pole

Przykład: Usunięcie atrybutu Salary z tabeli Etat4

ALTER TABLE Etat4
DROP COLUMN Salary;

Usunięcie tabeli (relacji) z bazy danych lub indeksu z tabeli - DROP

Usuwa istniejącą tabelę z bazy danych lub usuwa istniejący indeks z tabeli.
Składnia
DROP {TABLE tabela | INDEX indeks ON tabela}
Przed usunięciem tabeli lub też usunięciem z niej indeksu należy tabelę zamknąć.
Aby usunąć indeks z tabeli, można również użyć instrukcji ALTER TABLE.
Do utworzenia tabeli można użyć instrukcji CREATE TABLE, a do utworzenia indeksu instrukcji CREATE INDEX lub ALTER TABLE. W celu modyfikacji tabeli należy użyć instrukcji ALTER TABLE.

Przykład: Usunięcie tabeli Etat4

DROP TABLE Etat4;

 


Zapytania SQL-właściwe, łączenie tablic

Do zapytań SQL-właściwych należą 3 typy zapytań, które mogą być zdefiniowane tylko w języku SQL. Zapytania takie nie mogą być stworzone za pomocą szblonu QBE.

Są 3 typy zapytań SQL-właściwych:

Zapytania składające - suma (or) - UNION

Zapytanie składajace pozwala przedstawić w jednym arkuszu danych rezultat działania kilku zapytań wybierających. Każde z tych zapytań musi dawać zestawienie złożone z tej samej liczby kolumn, nie jest natomiast wymagane, by odpowiadające sobie kolumny poszczególnych zapytań zawierały dane tego samego typu. Oczywiście wymaga tego zdrowy rozsądek, cóż bowiem po zestawieniu, które w jednej kolumnie zawiera dane tekstowe, daty, kwort czy inne dane.

Stworzenie zapytania składającego polega na wpisaniu definicji poszczególnych zapytań wybierających i

SELECT etat from Pracownik
where id_zesp=10
union
select etat from pracownik
where id_zesp=30;

Zapytanie to można zapisać w innej postaci by uzyskać ten sam wynik, np.

SELECT distinct etat from Pracownik
where id_zesp=10 or id_zesp=30
order by etat;

lub

SELECT etat from Pracownik
where id_zesp in (10, 30)
order by etat;

Przykład25 (Boratyn). Zapytanie zestawiające listę nazwisk pracowników wraz z listą kursów przeprowadzonych przez nich w poszczególnych miesiącach pierwszego kwartału. Dane o kursach dla każdego miesiąca są przechowywane w odrębnej tabeli (Kursy-Styczeń, Kursy-Luty, Kursy-Marzec)

SELECT
Month([Kursy - Styczeń].[Początek]) AS [Miesiąc],
Personel.Nazwisko,
Count([
Kursy - Styczeń].[Prowadzący]) as [Ilość kursów]
FROM Personel
INNER JOIN [Kursy - Styczeń]
ON [Personel].[ID]=[Kursy - Styczeń].[Prowadzący]
GROUP BY Month([Kursy - Styczeń].[Początek]), Personel.Nazwisko
UNION
SELECT
Month([Kursy - Luty].[Początek]) AS [Miesiąc],
Personel.Nazwisko,
Count([
Kursy - Luty].[Prowadzący])
FROM Personel
INNER JOIN [Kursy - Luty]
ON Personel.[ID]=[Kursy - Luty].[Prowadzący]
GROUP BY Month([Kursy - Luty].[Początek]), Personel.Nazwisko
UNION
SELECT
Month([Kursy - Marzec].[Początek]) AS [Miesiąc],
Personel.Nazwisko,
Count([
Kursy - Marzec].[Prowadzący])
FROM Personel
INNER JOIN [Kursy - Marzec]
ON Personel.[ID]=[Kursy - Marzec].[Prowadzący]
GROUP BY Month([Kursy - Marzec].[Początek]), Personel.Nazwisko
ORDER BY [Miesiąc], [Ilość kursów] DESC , [Nazwisko];

W zapytaniu tym można wyróżnić 3 zwykłe zapytania zbiorcze, różniące sie od siebie jedną z tabel źródłowych. Każde z nich daje zestawienie nazwisk pracowników oraz liczbę kursów przeprowadzonych przez nich w miesiącu, do którego odnoszą się dane tabeli Kursy-Miesiąc. Pomiedzy definicjami poszczególnych zapytań składowych jest słowo UNION.

Rezultatem powyższego zapytania jest jedno zapytanie o nazwach kolumn odpowiadających nazwom pól określonym w pierwszym zapytaniu składowym: Miesiąc, Nazwisko, Ilość kursów. Za uporządkowanie wierszy w tym arkuszu odpowiada ORDER BY, na końcu zapytania składającego. Użyte w niej nazwy pól muszą być zgodne z nazwami użytymi w pierwszym z zapytań składowych. W przykładzie wiersze zestawienia uporządkowane są wg poszczególnych miesiecy, dla każdego miesiąca malejąco wg liczby kursów, w przypadku zaś tej samej liczby kursów - alfabetycznie wg nazwisk.

 

Zapytania definiujące dane (Data Definition)

SQL można wykorzystać do definicji struktur danych (tabel). Zapytania definiujące pozwalają definiować nowe tabele, dodawać nowe pola do projektu tabeli, określać pola klucza głównego, tworzyć indeksy, usuwać indeksy z tabel, usuwać całe tabele.

Podstawowe rodzaje konstrukcji w zapytaniach definiujących

Konstrukcja Zadanie konstrukcji Przykład(y)
CREATE TABLE Tworzy tabelę CREATE TABLE FirstTable (FirstName TEXT(20), LastName TEXT(30));
CREATE TABLE SecondTable (FirstName TEXT,
LastName TEXT, DateOfBirth DATETIME,
CONSTRAINT MyTableConstraint UNIQUE (FirstName, LastName, DateOfBirth));
ALTER TABLE Zmienia definicję istniejącej tabeli. Pozwala dodać/usunąć pole lub indeks. ALTER TABLE Etat4 ADD COLUMN Salary CURRENCY;
DROP TABLE Usuwa tabelę z bazy danych ALTER TABLE Orders DROP CONSTRAINT EmployeesOrders;
CREATE INDEX Tworzy indeks dla jednego lub kilku pol tabeli CREATE INDEX NewIndex ON Employees (HomePhone, Extension);
CREATE UNIQUE INDEX CustID ON Customers (CustomerID) WITH DISALLOW NULL;
CREATE UNIQUE INDEX OrderID ON OrderDetailsODBC (OrderID);
DROP INDEX Usuwa wybrany indeks tabeli DROP INDEX CustID ON Customers;

Tworzenie nowej tabeli - CREATE TABLE

Ogólna postać zapytania (wg Boratyna):
CREATE TABLE nazwa_tabeli
(pole1 typ1 [(rozmiar1)]
[CONSTRAINT indeks1
{PRIMARY KEY | UNIQUE | REFERENCES tabela_powiązana [(pole_odpowiadające)]
[, pole2 typ2 [(rozmiar2)] [CONSTRAINT...] [,...]]
[, CONSTRAINT indeks
{PRIMARY KEY (główne1 [, główne2 [,...] | UNIQUE (jednoznaczne1 [, jednoznaczne2 [,...] |
FOREIGN KEY (obcy1 [, ocy2 [,...]])
REFERENCES tabela_powiązana [(pole_odpowiadające1 [, pole_odpowiadające2 [,...]])]}
[,...]]):

Składnia wg pomocy Accessa)
CREATE TABLE tabela (pole_1 typ [(rozmiar)] [NOT NULL] [indeks_1] [, pole_2 typ [(rozmiar)] [NOT NULL] [
indeks_2] [, ...]] [, CONSTRAINT indeks_wielopolowy [, ...]])
Składnia instrukcji CREATE TABLE zawiera następujące elementy:
tabela - Nazwa tworzonej tabeli.
pole_1, pole_2 - Nazwa pola lub pól, które mają być utworzone w nowej tabeli. Należy utworzyć co najmniej jedno pole.
typ Typ danych pola w nowej tabeli.
rozmiar - Rozmiar pola w znakach (tylko dla pól typu Text lub Binary).
indeks_1, indeks_2 - Klauzula CONSTRAINT definiująca indeks dla pojedynczego pola.
indeks wielopolowy - Klauzula CONSTRAINT definiująca indeks dla wielu pól.
Użycie
Instrukcja CREATE TABLE jest używana do definiowana nowej tabeli wraz z polami i ograniczeniami nakładanymi na te pola. Jeśli dla pola określono ograniczenie NOT NULL, to nowe rekordy muszą zawierać w tych polach poprawne wartości.
Klauzula CONSTRAINT nakłada rozmaite ograniczenia na pole i może być użyta do ustalenia klucza podstawowego. Do utworzenia klucza podstawowego lub dodatkowych indeksów w istniejących tabelach można również użyć instrukcji CREATE INDEX.
Ograniczenie NOT NULL może być nałożone na pojedyncze pole lub też wystąpić w nazwanej klauzuli CONSTRAINT dotyczącej pojedynczego pola lub też wielu pól. Ograniczenie NOT NULL może być jednak nałożone tylko raz w odniesieniu do jednego pola, gdyż w przeciwnym przypadku wystąpi błąd wykonania.

Stworzenie tabeli nie jest tak skomplikowane, jak wynika ze schematu. Najważniejsze jest polecenie
CREATE TABLE tabela (pole1 typ1, pole2 typ2, ...).
Dla pól typu TEXT (tekstowego) trzeba podać liczbę określającą rozmiar danych, np. TEXT(25). Jeśli nie podamy rozmiaru, przyjęte zostanie domyślnie 255.

Przykład:
CREATE TABLE T1 (Nr integer, nazwa1 text(25), nazwa2 text);

  • Uwaga! Access nie akceptuje w wyrazeniu tego typu nazw pól ujętych w nawiasy prostokątne. Nazwy muszą być jednoczłonowe (bez spacji) i nie mogą zawierać polskich liter (np. ą, ł, ż)
  • Tabela to nie tylko lista pól, ale również klucz główny, indeksy i powiązania z innymi tabelami. Aby zdefiniować tabelę wraz z tymi strukturami, konieczne staje się użycie bloku CONSTRAINT (więzy), który może wystąpić w zapytaniu definiującym w 2 miejscach: dla dowolnego pola na liście pól nowej tabeli oraz na końcu zapytania.
    W pierwszym przypadku określa on dane pole jako:

    Jednocześnie deklarowane jest powiązanie tworzonej tabeli z tabelą o nazwie podanej po słowie REFERENCES. Nazwa odpowiadającego mu klucza tabeli powiązanej może byc opuszczona tylko wtedy, gdy jest to pole klucza głównego tej tabeli.

    Blok CONSTRAINT na końcu zapytania pełni podobną rolę, tyle że definiowane indeksy i powiązania mogą odnosić sie do kilku pól tworzonej tabeli. Nazwy tych pól muszą wtedy wymienione. Elementem obowiązkowym bloku jest nazwa indeksu lub powiązania, występująca bezpośrednio po słowie CONSTRAINT. Nie może się ona powtarzać dla indeksów tej samej tabeli ani dla powiązań. W tym miejscu dopuszczalne są już nazwy ujęte w nawiasy prostokątne.

    Przykład27 (Boratyn). Zapytanie definiujące tabelę Wykładowcy, złożoną z 2 pól: IDPracownika i IDKursu. Obydwa pola tworzą klucz główny, a każde z nich jest kluczem obcym, określającym powiązanie z tabelami, odpowiednio Personel i Oferta..

    CREATE TABLE [Wykładowcy]
    (
    IDPracownika INTEGER
    CONSTRAINT
    [Personel-Wykładowcy]
    REFERENCES
    [Personel] ([ID]),
    IDKursu BYTE
    CONSTRAINT
    [Oferta-Wykładowcy]
    REFERENCES
    [Oferta] ([ID Kursu]),
    CONSTRAINT
    [Wykładowcy-Główny]
    PRIMARY KEY
    (IDPracownika, IDKursu)
    );

    Zapytanie nie uruchomi się, gdy w bazie danych istnieje już tabela o nazwie identycznej z nazwą tabeli definiowanej.

    Zmiana definicji tabeli istniejącej - ALTER TABLE

    Zapytanie definiujace ma postać:
    ALTER TABLE nazwa_tabeli
    {ADD {COLUMN nazwa_pola typ_pola [(rozmiar_pola)} [CONSTRAINT...] | CONSTRAINT...} |
    DROP COLUMN nazwa_pola | CONSTRAINT nazwa_indeksu};

    Zapytanie umożliwia zmiany dwojakiego rodzaju w definicji tabeli. Dodanie (ADD) pola (COLUMN) lub indeksu/powiązania (CONSTRAINT) albi usunięcie (DROP) pola lub indeksu/powiązania. Dzięki takiemu zapytaniu możliwe staje się zadeklarowanie powiązania pomiędzy 2 istniejącymi tabelami bez użycia myszy!. Utorzone w ten sposób powiązania nie pozwalają jednak na kaskadowe usuwanie ani na kaskadową aktualizację. Trzeba włączyć odpowiednie opcje w polu Relacje.

    Przykłady: Przykład28, 28a, 28b: Zastąpienie klucza głównego tabeli Wykładowcy, złożonego z 2 pól (IDPracownika i IDKursu), przez klucz oparty na jednym polu o wartościach typu Licznik (Counter).

    Przykład28. Usunięcie klucza głównego opartego na 2 polach

    ALTER TABLE [Wykładowcy1]
    DROP
    CONSTRAINT [Wykładowcy-Główny];

    Przykład28a. Dodanie pola Numer kolejny, typu licznik

    ALTER TABLE [Wykładowcy]
    ADD
    COLUMN [Numer kolejny] COUNTER;

    Przykład28b. Dodanie klucza głównego, opartego na jednym polu Numer kolejny

    ALTER TABLE [Wykładowcy]
    ADD
    CONSTRAINT [Wykładowcy-Główny]
    PRIMARY KEY ([Numer kolejny]);

    Zapytania definiujące przeznaczone wyłącznie do tworzenia indeksów oraz ustawiania ich atrybutów

    Postać ogólna zapytania:
    CREATE [UNIQUE] INDEX nazwa_indeksu
    ON nazwa_tabeli (pole1 [, pole2 [,...]])
    [WITH {PRIMARY | DISALLOW NULL | IGNORE NULL}];

    Stworzony tym zapytaniem indeks może obejmować jedno lub kilka pól, wymienionych po nazwie tabeli. Aby indeks był jednoznaczny, trzeba użyć słowa UNIQUE lub formuły WITH PRIMARY. Wyrazenie WITH DISALLOW NULL odpowiada włączeniu atrybutu Wymagane (Required) dla danego pola tabeli (ale jej użycie nie powoduje włączenia tego atrybutu) i nie dopuszcza do pozostawienia pola bez wpisania wartości. Użycie WITH IGNORE NULL powoduje, że pola puste nie są uwzględniane w indeksie, co jest równoważne włączeniu atrybutu Ignoruj Null (Ignore Nulls) w polu dialogowym Indeksy. Z tych 3 wymienionych wyrażeń najbardziej restrykcyjne jest pierwsze, które nie pozwala na powtarzanie się tych samych wartości w indeksowanych polach i nie akceptuje pól pustych.
    Dzięki indeksom przyśpiesza sie operacje wyszukiwania danych wg określonych kryteriów a także zapobiega błędom wynikającym z powtórzeń tych samych wartości lub niewpisania w pole żadnej wartości.

    Przykład29. Utworzenie indeksu opartego na polach IDPracownika i IDKursu, w tabeli Wykładowcy - zapobiegnie powtórzenia identycznej kombinacji tych pól.

    CREATE UNIQUE INDEX
    [Wykładowca-Kurs]
    ON
    [Wykładowcy] (IDPracownika, IDKursu)
    WITH DISALLOW NULL;

    Zapytania usuwające indeksy - DROP INDEX albo całe tabele - DROP TABLE

    DROP {TABLE nazwa_tabeli | INDEX nazwa_indeksu ON nazwa_tabeli};

    Przykłady:

    Usunięcie indeksu Wykłądowca-Kurs z tabeli Wykładowcy:

    DROP INDEX [Wykładowca-Kurs] ON [Wykładowcy];

    Usunięcie tabeli Wykładowcy:

    DROP TABLE [Wykładowcy];


    SQL c.d.