PHP - kolejne wskazówki i rozwiązania w PHP i MySQL

DZIAŁANIE: brak działania
 

Kod:

brak kodu;

Ewentualne Objaśnienia:

Tutaj chciałem w tej jednej lekcji omówić kilka rzeczy związanych z zapytaniami, funkcjami i innymi rozwiązaniami stosowanymi w zapytaniach do bazy MySQL ( i nie tylko ). Czyli kolejna porcja takich ogólnych przydatnych rzeczy :

1). Zapewne kojarzycie bardzo przydatny operator LIKE klauzuli WHERE, jest ich jednak więcej i teraz je przedstawię :
- znaki równości, mniejszości itd czyli : =,>,<,>=,<=, !=, <>
- IS NOT NULL - sprawdza czy dane pole ma nadaną wartość
- IS NULL - sprawdza czy jest puste
- BETWEEN - na przykład wiek BTWEEN 18 AND 60 - wiadomo
- IN - czy wartość należy do określonego zbioru wartości na przykład : Nazwisko IN('Nowak','Kowalski')
- NOT IN - wiadomo
- LIKE - to już znacie
- NOT LIKE - wiadomo
- REGEXP - sprawdza czy wartość pasuje do danego wyrażenia regularnego, na przykład : Imie REGEXP '^Ma.*'

2). Pamiętacie złączenia ? Czyli sytuację kiedy na przykład chcemy wyszukać z 2 tabel, przykładowy zapis dla przypomnienia :
SELECT tresc.tytul, tresc.id FROM tresc, autorzy WHERE autorzy.nazwisko='Kowalski' AND autorzy.autorid = tresc.id

Jak pamiętacie separator kropki nie zawsze musi być używany, ale warto to robić bo :
- w tabelach mogą być pola o takich samych nazwach ( na przykład id i id )
- lepiej się w tym wszystkim połapać :)

3). Nie wspominałem wcześniej o tzw. left join . Jak wiecie bazy danych można sobie projektować bardzo rożnie, na przykład dla jakiegoś katalogu stron można zrobić dwie tabele jedna z linkami druga z kategoriami. Jak je teraz połączyć ? No choćby w tabeli z linkami dodać pole idkategorii. Jednak to nie jest dobre rozwiązanie - o wiele lepiej zrobić jeszcze jedną tabelę ( korzystamy ze złączeń tutaj tzw. wielu-do-wielu )o nazwie na przykład kategoria_link w której będziemy przechowywać pary kluczy, czyli id danego linka oraz id kategorii dla tego linka, to o wiele lepsze co nie ?
Jednak może być tak, że będziemy chcieli sobie wyświetlić na przykład linki, które nie są dopisane do żadnej kategorii itp - ogólnie mówiąc wyświetlenie danych ze sobą niezwiązanych. No i tutaj z pomocą przychodzi nam lef join - spowoduje wyszukanie wierszy spełniających zadany warunek połączenia między dwiema tabelami, ale jeżeli w drugiej nie ma odpowiadającego wiersza, do tabeli wynikowej zostanie dodany wiersz z zawartością NULL w prawej kolumnie.
Przykładowe zapytanie ( zaczerpnięte z Vademecum Profesjonalisty, wydanie III wyd.Helion ) :
SELECT klienci.klientid, klienci.nazwisko, zamowienia.zamowienieid FROM klienci lef join zamowienia on klienci.klientid=zamowienia.klientid

Pobieramy idklienta, nazwisko oraz numer (id) zamówienia z tabeli klienci poprzez LEFT JOIN z tabeli zamowienia ( specjalna klauzula ON ) naszym warunkiem jest id klienta równe id zamówienia. Łatwo wyobrazić sobie takie dwie tabele - klienci zawiera klientid, ale też zamowienia zawiera takie pole - dzięki left join jeżeli w tabeli zamowienia nie będzie id jakiegoś klienta - czyli jeszcze nie złożył zamówienia wyświetli się dla niego wartośc NULL dla pola zamowienieid, czyli tabelka typu :

klientid nazwisko zamowienieid
1 Nowak NULL
2 Kowalski NUL
3 Brożek 2
3 Brożek 3

Jak widać Pan Brożek zrealizował już 2 zamówienia, a Nowak i Kowalski żadnego.
Jeżeli chcielibyśmy uzyskać jedynie tych naszych obiboków, którzy nic nie zamawiają - czyli dla nich pole zamowienieid jest NULL można napisać :
SELECET klienci.klientid, klienci.nazwisko FROM klienci LEFT JOIN zamowienia using(klientid) WHERE zamowienia.zamowienieid is null; tutaj nie użyliśmy klauzuli ON jak wcześniej lecz USING - jednak pamiętajcie, że w przypadku using nie można podać nazwy tabeli, z której ma pochodzić pole łączące, pola te muszą mieć takie same nazwy w obu tabelach - bo jak wspominałem i w tabeli klienci i zamowienia jest pole o nazwie klientid - więc tutaj możemy śmiało użyć using niż pisać specjalnie, że klienci.klientid = zamowienia.klientid .
 

4). Aliasy - przy zapytaniach można użyć tzw. aliasów - odwołujemy się do tabel za pomocą innych nazw na przykład prosty przykładzik :
SELECT a.nazwisko FROM autorzy as a, tresci as t WHERE a.id=t.numer; poprzez słowo AS nadajemy odpowiedni aliasy dla tabel ( tutaj autorzy oraz tresci )

5). Bardzo przydatne są funkcje, słowa służące do grupowania i agregowania danych. Na przykład mamy jakąś tabele zawodników gdzie znajduje się kilkanaście rekordów z ich punktami, chcemy obliczyć z całej tabeli średnią punktów. Piszemy więc zapytanie :
SELECT avg(punkty) FROM zawodnicy; - słowo kluczowe AVG oblicza wartość średnią we wskazanej kolumnie, czyli tutaj w kolumnie punkty, w tabeli zawodnicy.

Ale na przykład może być tak, że w naszej tabeli jest bardzo dużo zawodników i każdy ma jeszcze przypisany iddruzyny - czyli jak się domyślacie id drużyny w jakiej ten zawodnik gra. Chcielibyśmy więc dla każdej drużyny ( czyli każdego z tych id ) obliczyć tą naszą wartość średnią więc piszemy :
SELECT iddruzyny, avg(punkty) FROM zawodnicy GROUP BY iddruzyny; - w ten sposób otrzymamy dla każdego id z tabeli obliczoną średnią wartość punktów, będziemy mogli swobodnie wyświetlić id oraz średnią

Trzeba pamiętać o pewnej rzeczy - standard ANSI SQL określa iż w klauzuli SELECT, która wykorzystuje agregację danych lub grupowanie - mogą znajdować się jedynie funkcje agregującą oraz nazwy kolumn, które będą podstawą grupowania ( nazwy te muszą być również wyszczególnione w klauzuli SELECT - tak jak w powyższym przykładzie ).
Jednak MySQL ma troszkę swobodniejsze możliwości - obsługuje tzw. składnię rozszerzoną - pozwala na ominięcie nazw kolumn w klauzuli SELECT, jeżeli nie ma potrzeby ich wyświetlania.

Istotną sprawą jest klauzula HAVING pozwalająca na określanie dodatkowych kryteriów zapytania. Powinna ona znaleźć się zaraz za klauzulą group by; działa ona tak samo jak WHERE w zapytaniach gdzie nie ma agregacji czy grupowania. Tak więc przykładowo robimy grupowanie drużyn dla nich średnie wartości, ale jedynie kiedy średnia wartość punktów jest większa od 15 :
SELECT idruzyny, avg(punkty) FROM zawodnicy GROUP BY iddruzyny HAVING avg(punkty)>15; - pamiętajcie, że having odnosi się tylko do grup danych.

Funkcje agregujące w MySQL :
- avg - wiadomo
- count - zwraca liczbę niezerowych wartości w danej kolumnie, jeżeli nazwę kolumny poprzedzimy słowem distinct to funkcja zwróci nam wartość jedynie występujących odrębnych wartości; jeżeli zamiast nazwy kolumny wpiszemy * zwrócona zostanie liczba wierszy w tabeli
- min - wiadomo
- max - wiadomo
- std - oblicza odchylenie standardowe wartości w kolumnie
- stddev - jak wyżej
- sum - suma wartości w kolumnie

6). Ciekawą sprawą są tzw. pod-zapytania czyli zagnieżdżanie jednego zapytania w drugim - jest to często bardzo przydatne, choć takie same rozwiązania można uzyskać poprzez odpowiednie zapytanie korzystające ze złączeń, jednak skorzystanie z pod-zapytania jest o wiele wygodniejsze i czytelne.
Przykładowo chcemy wyświetlić z naszej tabeli zawodników tych, którzy uzbierali największą ilość punktów. Oczywiście możemy użyć grupowania malejącego oraz na przykład limit ilości zwróconych danych, ale możemy też zapytać :
SELECT idzawodnika, punkty FROM zawodnicy WHERE punkty=(select max(punkty) from zawodnicy); - Jak widać bardzo prosta sprawa, pobieramy punkty oraz id danego zawodnika, dla którego punkty są równe max wartości z kolumny punkty - co też obliczamy zagnieżdżonym zapytaniem .

7). Kolejna sprawa to tzw. operatory pod-zapytań  - 4 z nich można użyć również w zapytaniach zwykłych, zobaczmy jakie to operatory :
- ANY - zwraca wartość TRUE jeżeli porównanie zwróci true dla dowolnego wiersza z pod-zapytania przykładowo :
SELECT p1 FROM t1 WHERE p1 > ANY (SELECT p1 FROM t2); - czyli zwraca wartość jeżeli jakiekolwiek pole p1 z tabeli t1 będzie większe od pola p1 z tabeli t2
- IN - jest to pewien równoważnik odpowiadający zapisowi " =ANY "
- SOME - to po prostu alias zapytania ANY
- ALL zwraca true, jeżeli porównanie będzie miało wartość true dla wszystkich wierszy z podzapytania

8). Mamy także tzw. podzapytania skorelowane - tutaj element z zapytania zewnętrznego może zostać użyty w zapytaniu zewnętrznym, przykładowo :
SELECT idzawodnika, nazwisko, punkty FROM zawodnicy WHERE not exists (SELECT * from osiagniecia WHERE osiagniecia.idzawodnika=zawodnicy.idzawodnika); Exists zwraca true jeżeli w podzapytaniu znajduje się co najmniej 1 wiersz pasujący ( do zapytania zewnętrznego). NOT Exists - odwrotnie rzecz jasna. Tutaj mamy działanie takie jakie uzyskiwaliśmy lewym złączeniem - chcemy pobrać tych zawodników, którzy nie mają na koncie żadnego osiągnięcia - czyli zapytanie (wynik) podzapytanie nie pasuje do wyniku z zapytania zewnętrznego

9). Przydatna rzecz to podzapytania wierszowe -  na przykład może być tak, że chcemy porównać dwa wiersze z dwóch tabel przykładowo :
SELECT pol1, pol2, pol3 FROM t1 WHERE (pol1,pol2,pol3) IN (SELECT pol1, pol2, pol3 FROM t2); - czyli jak widać zwracane są wiersze, które są identyczne w tabeli t1 i t2, IN przypominam oznacza =ANY - czylu jakikolwiek wiersz (pol1,pol2,pol3) tabeli t1 jest równy takiemu wierszowi w t2

10). Kolejna możliwość to tzw. tabela tymczasowa - możliwość wykonania zapytania na wynikach innego zapytania (pod-zapytania), które jest wtedy traktowane jako tabela tymczasowa ( jego wyniki ) przykładowo :
SELECE * FROM (select idzawodnika, nazwisko, imie from zawodnicy where wojewodzctwo='slaskie') as zawodnicy_slaskie; - jak widać podzapytanie zostało umieszczone w klauzuli FROM. Zaraz za nawiasem zamykającym podzapytanie należy podać jego wynikom alias. Ten przykład jest akurat bardzo prosty, można było taki efekt uzyskać jednym zapytaniem, jednak do zobrazowania idei taki przykład myślę jest odpowiedni.