SELECT WHERE w SQL służy do ograniczania wyników zapytania. Wykorzystanie SELECT WHERE jest w bazach danych jest niezwykle często wykorzystywane ponieważ pozwala sterować które rekordy i pod jakimi warunkami mogą zostać zwrócone w wyniku zapytania. W poniższym kursie dotyczącym WHERE:
- Przygotuję bazę
- Opiszę podstawową składnię SELECT WHERE w tym
- Przedstawię porównania =
- Poznasz co oznacza błąd: ORA-01722: niepoprawna liczba
- Pokażę zaprzeczenie porównania czyli <>
- Pokażę jak wybrać większe i mniejsze rekordy przy użyciu znaków > i <
- Przedstawię działanie operatora WHERE BETWEEN
- Pokażę, jak łączyć warunki przy pomocy operatorów logicznych AND i OR
- Podsumuję cały kurs
- Przedstawię ćwiczenie + jego rozwiązanie
Ten kurs jest częścią cyklu Kurs SQL
Zapisz się na autorskie Szkolenie SQL
Przygotowanie bazy danych
We wszystkich kursach korzystam z tej samej bazy danych którą w trakcie kursów sukcesywnie rozszerzam. Schemat testowej bazy danych został opracowany w kursie dotyczącym normalizacji baz danych. Jeżeli już masz przygotowaną bazę danych jak poniżej pomiń ten krok.
Aby utworzyć powyższy schemat bazy danych należy wykonać skrypt z ćwiczenia z kursu SQL DROP | CREATE TABLE (Dodawanie i usuwanie tabel):
CREATE TABLE CENNIK (
ROZMIAR VARCHAR2(3 CHAR)
, CENA NUMBER
, CONSTRAINT CENNIK_PK PRIMARY KEY (ROZMIAR)
);
CREATE TABLE MENU (
ID NUMBER GENERATED ALWAYS AS IDENTITY
, NAZWA VARCHAR2(20 CHAR)
, ROZMIAR VARCHAR2(3 CHAR)
, CONSTRAINT MENU_PK PRIMARY KEY (ID)
, CONSTRAINT MENU_CENNIK_FK FOREIGN KEY (ROZMIAR) REFERENCES CENNIK(ROZMIAR)
);
CREATE TABLE KLIENT (
ID NUMBER GENERATED ALWAYS AS IDENTITY
, IMIE VARCHAR2(20 CHAR)
, NAZWISKO VARCHAR2(20 CHAR)
, CONSTRAINT KLIENT_PK PRIMARY KEY (ID)
);
CREATE TABLE ADRES (
ID_ADRESU NUMBER GENERATED ALWAYS AS IDENTITY
, ULICA VARCHAR2(20 CHAR)
, NR_BLOKU VARCHAR2(10 CHAR)
, NR_MIESZKANIA VARCHAR2(10 CHAR)
, MIASTO VARCHAR2(20 CHAR)
, CONSTRAINT ADRES_PK PRIMARY KEY (ID_ADRESU)
);
CREATE TABLE ZAMOWIENIA (
ID_PIZZA NUMBER
, ID_KLIENTA NUMBER
, ID_ADRESU NUMBER
, DATA_ZAMOWIENIA DATE
, CONSTRAINT ZAM_PIZZA_FK FOREIGN KEY (ID_PIZZA) REFERENCES MENU(ID)
, CONSTRAINT ZAM_KLIENT_FK FOREIGN KEY (ID_KLIENTA) REFERENCES KLIENT(ID)
, CONSTRAINT ZAM_ADRES_FK FOREIGN KEY (ID_ADRESU) REFERENCES ADRES(ID_ADRESU)
);
Natomiast do wypełnienia danych posłuży skrypt z ćwiczenia z kursu SQL INSERT | DELTE | UPDATE (Modyfikacja danych tabel):
INSERT INTO CENNIK (ROZMIAR, CENA) VALUES ('L', 15);
INSERT INTO CENNIK (ROZMIAR, CENA) VALUES ('XL', 20);
INSERT INTO CENNIK (ROZMIAR, CENA) VALUES ('XXL', 25);
INSERT INTO MENU (NAZWA, ROZMIAR) VALUES ('Margherita', 'XXL');
INSERT INTO MENU (NAZWA, ROZMIAR) VALUES ('Margherita', 'L');
INSERT INTO MENU (NAZWA, ROZMIAR) VALUES ('Pepperonia', 'L');
INSERT INTO MENU (NAZWA, ROZMIAR) VALUES ('Hawajska', 'L');
INSERT INTO MENU (NAZWA, ROZMIAR) VALUES ('Margherita', 'XL');
INSERT INTO MENU (NAZWA, ROZMIAR) VALUES ('Wiejska', 'XXL');
INSERT INTO KLIENT (IMIE, NAZWISKO) VALUES ('Jan', 'Kowalski');
INSERT INTO KLIENT (IMIE, NAZWISKO) VALUES ('Piotr', 'Nowak');
INSERT INTO KLIENT (IMIE, NAZWISKO) VALUES ('Anna', 'Zaradna');
INSERT INTO KLIENT (IMIE, NAZWISKO) VALUES ('Kamila', 'Zaradna');
INSERT INTO ADRES (ULICA, NR_BLOKU, NR_MIESZKANIA, MIASTO) VALUES ('Hery', 5, null, 'Warszawa');
INSERT INTO ADRES (ULICA, NR_BLOKU, MIASTO) VALUES ('Domaniewska', 2, 'Poznań');
INSERT INTO ADRES (ULICA, MIASTO) VALUES ('JP2', 'Warszawa');
INSERT INTO ADRES (ULICA, MIASTO) VALUES ('JP2', 'Wrocław');
INSERT INTO ZAMOWIENIA (ID_PIZZA, ID_KLIENTA, ID_ADRESU, DATA_ZAMOWIENIA) VALUES (1, 1, 1, '2019-01-01');
INSERT INTO ZAMOWIENIA (ID_PIZZA, ID_KLIENTA, ID_ADRESU, DATA_ZAMOWIENIA) VALUES (2, 2, 2, '2019-01-02');
INSERT INTO ZAMOWIENIA (ID_PIZZA, ID_KLIENTA, ID_ADRESU, DATA_ZAMOWIENIA) VALUES (3, 2, 2, '2019-01-02');
INSERT INTO ZAMOWIENIA (ID_PIZZA, ID_KLIENTA, DATA_ZAMOWIENIA) VALUES (4, 3, '2019-01-03');
INSERT INTO ZAMOWIENIA (ID_PIZZA, ID_KLIENTA, DATA_ZAMOWIENIA) VALUES (5, 3, '2019-01-03');
INSERT INTO ZAMOWIENIA (ID_PIZZA, ID_KLIENTA, DATA_ZAMOWIENIA) VALUES (6, 3, '2019-01-03');
INSERT INTO ZAMOWIENIA (ID_PIZZA, ID_KLIENTA, ID_ADRESU, DATA_ZAMOWIENIA) VALUES (2, 3, 3, '2019-01-05');
INSERT INTO ZAMOWIENIA (ID_PIZZA, ID_KLIENTA, ID_ADRESU, DATA_ZAMOWIENIA) VALUES (3, 4, 4, '2019-01-06');
COMMIT;
Przed rozpoczęciem tego kursu warto zapoznać się z kursem dotyczącym pobierania danych z tabeli przy pomocy klauzuli SELECT FROM
Składnia SQL SELECT WHERE
Znając już strukturę bazy danych i przechowywanych w niej danych możemy opracować zapytanie które pozwoli wyświetlić tylko część rekordów z tabeli bazy danych. Podstawowa struktura zapytania SELECT WHERE wygląda następująco:
SELECT kolumna_1 , kolumna_2 ... , kolumna_n FROM nazwa_tabeli WHERE warunek_1 operator_logiczny warunek_2 ... operator_logiczny warunek_n;
Struktura polecenia jest rozszerzeniem SLECET FROM. Klauzula WHERE służy do ograniczania wyników zapytania. Po klauzuli WHERE należy podać pierwszy warunek a następnie operator logiczny i kolejny warunek. Operatorów logicznych i warunków może być nieskończenie wiele.
SELECT WHERE =
Pierwszym ograniczeniem które poznamy jest równoważność oznaczone znakiem “=”. Po jednej stronie podajemy kolumnę a po drugiej wartość jaką powinna przyjąć kolumna aby rekord został zwrócony do wyniku zapytania.
Dla przykładu postarajmy się wybrać wszystkie osoby o imieniu “Jan” z tabeli KLIENT. Przykładowe zapytanie może wyglądać jak poniżej:
SELECT
ID
, IMIE
, NAZWISKO
FROM
KLIENT
WHERE
IMIE = 'Jan';
oraz wynik:
Tabela KLIENT posiada 4 rekordy jednak tylko jeden gdzie kolumna IMIE ma wartość ‘Jan’.
Aby poprawnie przyrównać wartości kolumn należy zwrócić uwagę na jej typ, dlatego też porównując do kolumn typu:
- CHAR, VARCHAR2 porównujemy wartości w cudzysłowach ” np. =’Jan’. Wartość przyrównywana jest czuła na wielkość liter dlatego ‘JAN’ i ‘Jan’ to inne wartości.
- NUMBER przyrównujemy do wartości numerycznej bez użycia cudzysłowów np. = 10
- Dla kolumn typu DATE powinniśmy porównać do wartości datowej np. = sysdate
ORA-01722: niepoprawna liczba
Co w takim razie gdy podamy zły typ danych w porównaniu jak w poniżej:
SELECT
ID
, IMIE
, NAZWISKO
FROM
KLIENT
WHERE
IMIE = 1;
Jako, że kolumna IMIE jest typu varchar2 a porównujemy ją do wartości liczbowej 1 to baza danych zwróci nam błąd:
ORA-01722: niepoprawna liczba 00000 - "invalid number" *Cause: The specified number was invalid. *Action: Specify a valid number.
Błąd ten oznacza, że przyrównujemy zły typ danych do kolumny.
Uwaga: Baza danych potrafi w niektórych przypadkach niejawnie przekonwertować liczbę na znaki dlatego dla przyrównania =’1′ możemy otrzymać ten sam wynik co dla =1 zgodnie z poniższym przykładem:
Pomimo, że kolumna ID jest typu number a ‘1’ jest typu znakowego to baza potrafiła przekonwertować znak ‘1’ na liczbę 1.
SELECT WHERE <>
Kolejnym ograniczeniem jest “różne niż” czyli znak <>. W wielu językach programowania porównanie tego typu oznaczone jest znakami (wykrzyknik równa się) != ale w bazie danych oracle jest to znacznik <>. Użycie tego porównania zwraca nam rekord do wyniku zapytania w sytuacji gdy lewa i prawa porównania stroną są różne.
Dla przykładu wyciągnijmy wszyskich klientów którzy nie mają na imię ‘Jan’, zapytanie może wyglądać jak poniżej
SELECT
ID
, IMIE
, NAZWISKO
FROM
KLIENT
WHERE
IMIE <> 'Jan';
oraz wynik zapytania
Powyższe zapytanie zwróciło nam wszystkie rekordy w których kolumna IMIE przyjmuje wartości inne niż ‘Jan’
SELECT WHERE > <
Kolejnym ograniczeniem będą znaki większości > i mniejszości <. Służą one do porównywania wartości numerycznych lub dat. Porównania te nie są włączne ( ang. inclusive) tzn. 20 > 20 nie zwróci rekordu do wyniku zapytania. Dlatego aby zmienić porównanie na zawierające skrajną wartość należy dodać znak = np. >= lub <= w tym przypadku 20 >= 20 zwróci rekord.
Napiszmy w takim razie zapytanie które zwróci z tabeli CENNIK takie rekordy gdzie kolumna CENA ma wartość większą 20. Przykładowe zapytanie może wyglądać jak poniżej:
SELECT
*
FROM CENNIK
WHERE CENA > 20;
oraz wynik:
W tabeli CENNIK mamy łącznie 3 rekordy w tym tylko jeden dla który w kolumnie CENA przyjmują wartość większą niż 20.
Zobaczmy jeszcze jak może wyglądać zapytanie gdybyśmy chcieli zwrócić rekordy dla których cena jest większa lub równa 20:
SELECT
*
FROM CENNIK
WHERE CENA > 20;
oraz wynik:
SELECT WHERE BETWEEN
Połączeniem ograniczenia > i < jest ograniczenie BETWEEN wartość_1 AND wartość_2 które mówi, że kolumna jest z przedziału dwóch liczb.
Dla przykładu poszukajmy więc w cenniku wartości CENA pomiędzy 20 a 30. Zapytanie może wyglądać jak poniżej:
SELECT
*
FROM CENNIK
WHERE CENA BETWEEN 20 AND 30;
oraz wynik:
Jak widzimy klauzula BETWEEN również nie jest włączna. Klauzula BETWEEN nie występuje w wersji włacznej (ang. inclusive), aby spełnić warunek z włączeniem wartości brzegowych należy połączyć dwa warunku >= oraz <= jednak o łączeniu warunków w poniższym rozdziale o operatorach logicznych.
Łączenie warunków przy pomocy operatorów logicznych AND i OR
Wszystkie powyżej przedstawione sposoby ograniczania rekordów można łączyć ze sobą przy pomocy operatorów logicznych AND i OR. Nim jednak przejdę do przykładów krótkie przypomnienie które co zwraca AND i OR dla poszczególnych wartości logicznych:
A | B | A AND B | A OR B |
true | true | true | true |
true | false | false | true |
false | true | false | true |
false | false | false | false |
Łączenie warunków ograniczających rekordy w zapytaniu polega na łączeniu operatorami logicznymi AND i OR warunków. Jeżeli warunek jest spełniony zwraca ono wartość true jeżeli natomiast nie jest spełniony to zwraca wartość false. Dzięki temu możemy łączyć warunki.
Uwaga: Jakiekolwiek porównanie do wartości NULL zwraca wartość NULL co nie jest ani true ani false. O logice trójargumentowej i NULL w bazie Oracle opiszę w innym kursie. Narazie pomijam przypadki z nullem.
Spróbujmy więc znaleźć w tabeli KLIENT osoby które mają na imię ‘Jan’ lub nazwisko ‘Zaradna’. Warunek lub oznacza, że należy wykorzystać operator OR. Zobaczmy jak może wyglądać przykładowe zapytanie:
SELECT
ID
, IMIE
, NAZWISKO
FROM
KLIENT
WHERE
IMIE = 'Jan'
OR NAZWISKO = 'Zaradna';
oraz wynik:
Powyższe zapytanie zwróciło nam 3 rekordy ponieważ każdy z nich spełnia powyższy warunek. Jedynym rekordem nie spełniającym warunków z zapytania jest Piotr Nowak ponieważ ani jego imie ani nazwisko nie zgadza się z warunkami.
Zapytanie zwracające Piotra Nowaka możemy skonstruować przy pomocy operatora AND:
SELECT
ID
, IMIE
, NAZWISKO
FROM
KLIENT
WHERE
IMIE = 'Piotr'
AND NAZWISKO = 'Nowak';
oraz wynik:
Jak widzimy jedynie jeden rekord spełnia jednocześnie warunek IMIE = ‘Piotr’ i NAZWISKO = ‘Nowak’
Znając już działanie tych operatorów napiszmy zapytanie które zwróci nam z tabeli CENNIK wartości od 20 do 40 włącznie po obu stronach. Przykładowe zapytanie może wyglądać jak poniżej:
SELECT
*
FROM CENNIK
WHERE CENA >= 20
AND CENA <= 40;
oraz wynik:
W powyższym wyniku widzimy wszystkie rekordy z tabeli CENNIK gdzie cena zaczyna się od 20 włącznie i jest mniejsza niż 40 również włącznie.
Podsumowanie
Z powyższego kursu SELECT WHERE nauczyłeś się, jak ograniczać wyniki zapytania a w szczególnośći:
- Ograniczeniu kolumny do konkretnej wartości służy operator “=”
- Wyszukując wartości “różnych od” służy operator “<>”
- Do wyszukiwania większych i mniejszych wartości służą operatory “>” i “<“
- Klauzula WHERE BETWEEN pozwala znaleźć rekordy z zakresu dwóch wartośći
- W ograniczeniach należy zwracać uwagę na typ danych i tak dla:
- Kolumn tekstowych wartości wpisujemy w pojedynczym cudzysłowiu np. = ‘Jan’
- Kolumn numerycznych podajemy wartośc numeryczną np. = 10
- Gdy w porównaniu podamy zły typ baza danych zwróci błąd: ORA-01722: niepoprawna liczba
- Do łączenia warunków służą operatory:
- AND – zwraca rekordy tylko gdy oba warunki są spełnione
- OR – zwraca rekord gdy przynajmniej jeden warunek jest spełniony
Ćwiczenie
- Z tabeli MENU znaleźć wszystkie pizze w rozmiarze “L”
- Znaleźć w tabeli ADRES w mieście Warszawa i ulicy Hery
- Znaleźć w tabeli CENNIK z przedziału od 0 do 20 bez wartości granicznych.
Odpowiedzi:
- Z tabeli MENU znaleźć wszystkie pizze w rozmiarze “L”
SELECT
*
FROM MENU
WHERE ROZMIAR = 'L';
2. Znaleźć w tabeli ADRES w mieście Warszawa i ulicy Hery
SELECT
*
FROM ADRES
WHERE MIASTO = 'Warszawa'
AND ULICA = 'Hery';
3. Znaleźć w tabeli CENNIK z przedziału od 0 do 20 bez wartości granicznych.
SELECT
*
FROM CENNIK
WHERE CENA BETWEEN 0 AND 20;
lub
SELECT
*
FROM CENNIK
WHERE CENA > 0
AND CENA < 20;