Temat: Wprowadzenie do języka zapytań SQL: wybór danych (SELECT), tworzenie tabeli (CREATE), modyfikacje danych w tabeli: INSERT, DELETE, UPDATE. Modyfikacja struktury.
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.
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ół;
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;
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 - 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] |
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] SELECT Personel.Nazwisko,
Personel.Imię, Płace.Zasadnicza, Personel.ID |
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:
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, 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];
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ę:];
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];
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, 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 |
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.
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;
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] );
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.
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.
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;
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:];
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;
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;
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:
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.
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; |
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);
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.
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]);
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;
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];