Im więcej nietypowych rzeczy programuję, tym więcej nietypowych problemów musze pokonać. Co powiecie na sumę 2 liczb, z których jedna jest wartością NULL powstałą w wyniku działania SUM() lub pochodnych, gdzie nie odnaleziono żadnego rekordu.
Badamy:
SELECT 1+2+3
>> 6
SELECT 1+2+NULL
>> NULL
SELECT COALESCE( NULL, 0 )
>> 0
Zatem analogicznie do powyższego przykładu:
UPDATE users SET user_points = user_points + COALESCE((SELECT SUM( ... ) WHERE ...), 0)
Punkty użytkownika już zawsze będą się sumowały poprawnie :-)
Ostatnimi czasy potrzebowałem danych z sąsiedniej tabeli przy UPDATE jedngo z pól w bazie danych. Danych do przetworzenia było sporo, więc zwracałem uwagę na wydajność zapytania. Aby zebrać potrzebne informacje, można użyć jednego ze sposobów:
Kartkując manual nie natrafiłem się w standardowej dokumentacji na nic konkretnego, aż nie spojrzałem na bardzo przydatne komentarze użytkowników. Okazało się, że przy UPDATE można wykonywać dowolne JOIN‘y, schemat jest następujący:
UPDATE TABLE JOIN another_table SET ...
W tym momencie mamy do dyspozycji wszystkie pola z dołączonej tabeli. Bardzo przydatne.
Przykład z życia.
Miałem za zadanie odznaczyć typy bukmacherskie na trafione, nietrafione, odwołane z przyczyn odwołania całego meczu piłkarskiego oraz te, które jeszcze nie mogą zostać oznaczone jako trafione lub nie, gdyż mecz się jeszcze nie odbył.
UPDATE typer_tickets_items LEFT JOIN typer_events ON(event_id = item_event) SET item_status = ( CASE WHEN event_status IS NULL THEN NULL # mecz nie zostal rozegrany WHEN event_status = -1 THEN -1 # mecz anulowany WHEN event_status = item_bet THEN 1 # typ trafiony ELSE 0 END # typ nietrafiony ) WHERE item_status IS NULL
Mam nadzieję, że komuś się przyda…
Podobnie jak w PHP, baza danych MySQL ma odpowiednik if, czyli przypadków (inaczej serii warunków, instrukcji warunkowych). Różnicą między implementacją CASE‘a w MySQL i ifa PHP jest to, że baza danych zwraca konkretną wartość z case’a, a nie wykonuje dowolnej ilości dowolnych akcji.
CASE Syntax:
Najprostsza struktura CASE’aprzedstawia się nastepująco:
CASE WHEN [conditions] THEN ... ELSE ... END
Składnia powinna rozpocząć się słowem kluczowym CASE, a zakończyć END. Pomiędzy znajdują się warunki WHEN oraz operacja zwrócenia odpowiedniej wartości, która po nich następuje THEN (mamy możliwość uwzględnić nieskończenie wiele warunków). Jeżeli żaden warunek nie zostanie spełniony możemy użyć opcjonalnie ELSE.
Przykłady z życia.
Wyobraźmy sobie, że mamy posortować listę aukcji przedmiotów na Allegro od najtańszych, do najdroższych. Należy założyć, że są 2 typy aukcji: kup teraz i licytacja. Pole licytacji w bazie danych zawiera największą zaproponowaną kwotę przez użytkowników w procesie licytacji, a cena kup teraz ustalana jest przez sprzedającego. Są to dwa różne pola w bazie danych, a jedno kryterium sortowania, dlatego trzeba scalić cenę w jedną, wybierając odpowiednią. Musimy przewidzieć sytuację, w której aukcja jest typu kup teraz oraz licytacji, wówczas jeżeli najwyższa oferta jest większa od ceny kup teraz, wówczas wybieramy pole z największa propozycją:
SELECT ( CASE WHEN (auction_type = 'bidding' OR auction_price_bid > auction_price_buynow) THEN auction_price_bid ELSE auction_price_buynow END) AS auction_price
Stworzyliśmy pole auction_price, po którym można sortować aukcje od najtańszej do najdroższej i na odwrót.
Mam nadzieję, że krótki wpis przyda się początkującym. Nic więcej nie trzeba opisywać, temat wydaje się co najmniej trywialny.
Powstało masę artykułów na temat MVC, temat staje się naprawdę oklepany. Postanowiłem zebrać wszystkie informacje w jedno miejsce i streścić je w jednym artykule uzupełniając go o informacje, które nabyłem z własnego doświadczenia oraz zwracając uwagę na najistotniejsze informacje.
Czym jest model
Model to jedna z warstw wzorca projektowego MVC, który odpowiada logikę biznesową, czyli pozyskiwanie oraz modelowanie danych pozyskanych ze źródła danych. Na samym wstępie brzmi to bardzo abstrakcyjnie. W myśl architektury MVC, dostęp do modelu powinien mieć tylko kontroler, a w żadnym wypadku widok. Dodatkowo model musi pobrać i modelować dane w taki sposób, aby można było go ewentualnie wymienić bez jakiejkolwiek ingerencji w kontroler, a co za tym idzie – widok. Niezależnie od tego, z jakiego źródła informacji korzysta (pliki tekstowe, bazy danych, pliki XML) kontroler powinien otrzymać maksymalnie zbliżone dane podczas wymiany źródła informacji.
Model != baza danych
Często spotykam się z definicją modelu jako źródłem połączenia i wykonywania zapytań do serwera bazy danych. Otóż nie jest to prawdą. Według ideologii MVC model powinien być jedynie pośrednikiem między warstwą aplikacji przeznaczoną do połączenia do bazy danych, wykonywania zapytań itp., a kontrolerem. Dodatkowo powinien pomóc kontrolerowi w zbudowaniu zapytania do źródła informacji (pobranie danych na podstawie kryteriów), zmodelować je i zwrócić. Dlaczego model nie jest połączeniem do bazy danych? Jeżeli model potraktujemy jako pośrednika między kontrolerem a źródłem danych, ma on prawo wybrać dowolny sposób uzyskania żądanych informacji. Wcale nie oznacza to, że model musi używać baz danych, ale może użyć plików XML lub API udostępniane przez konkretny serwis (np. YouTube)
Wymienialność modeli i modelowanie danych
Modelowanie informacji jest to dostosowanie ich do użytku przez kontroler. Zazwyczaj jest to przekazywanie informacji w postaci tablic, wartości logicznych, liczb i ciągów znaków. Przykładem może być pobieranie informacji z bazy danych. Kontroler de facto nie wie skąd są pobierane dane, wie to tylko model, otrzymuje suche informacje. Jak rozumieć modelowanie danych przy projektowaniu aplikacji? Wyobraźmy sobie sytuację, że zmieniamy źródło informacji z bazy danych na pliki XML. W tym przypadku kontroler powinien otrzymać rekordy danych jako tablica o tych samych kluczach i tych samych typach danych, jak miało to miejsce przy używaniu bazy danych. Wymiana modelu odbywa się bez ingerowania w kontroler.
Przykłady modeli
Najpopularniejszym sposobem pozyskania informacji jest połączenie do bazy danych i pobieranie (reprezentowanie) ich na różnoraki sposób. Doskonale wyjaśnia to tekst znajdujący się w wikipedii:
Frameworki MVC do operacji na bazach danych używają modeli i mapowania relacyjno-obiektowego, ORM (ang. object-relationship mapping) – w Railsach jest to ActiveRecord, w Catalyscie np. DBIx::Class, a framework Spring w Javie używa Hibernate. Zwykle jest też możliwe użycie baz danych przez bezpośrednie zapytania SQL. Użycie modeli upraszcza typowe operacje – wyświetlanie ze stronicowaniem, edycję danych, a także uniezależnia od konkretnego typu bazy danych.
Posiadam przykład od siebie. Źródłem danych jest API serwisu Last.fm:
Dziś otwarty został sklep.php.pl, którego mały engine miałem okazję pisać. Póki co można w nim nabyć kubek-termos programatora o szczelnym, plastikowym zamknięciu zapobiegającym rozlewaniu się zawartości:
Cena z przesyłką wynosi 40zł. Mam nadzieję, że z biegiem czasu asortyment znacznie się powiększy. Warto wspomnieć, że z każdym złożonym zamówieniem zasilasz pulę pieniężną przeznaczoną na nagrody w konkursach o 5 zł.
Wiele razy zdarzało mi się, że mój serwer padł całkowicie lub przerywał żądania. Wówczas był niedostępny, a zarobki generowane z serwisów diametralnie spadały. Co więcej… użytkownicy poczuli niestabilność maszyny. Dziś po odpowiedniej optymalizacji kodów aplikacji pady są rzadkością, ale wolę się ubezpieczyć przed niespodziewanym downem serwera.
Problem można rozwiązać w prosty sposób: cyklicznie uruchamiany program bash‘a przez crona będzie odpowiadał za poprawne działanie usługi apache. Listing, który zaprezentuję łączy się z adresem url odwołującym się do naszego serwera za pomocą wget, a następnie wynik działania (response body) zapisze do pliku tymczasowego. Jeżeli plik istnieje oraz ma rozmiar niezerowy, oznacza to, że serwer działa poprawnie. Jeżeli plik nie istnieje, bądź jego wielkość jest równa zero z, oznacza to, że trzeba zrestartować usługę apache, bo nie odpowiada. Zaraz przed zakończeniem programu, plik tymczasowy powinien zostać usunięty. Pozwoliłem sobie opublikować mały program służący do automatycznego restartu usługi apache.
Aby nasz program poprawnie działał, trzeba zastanowić się nad trzema istotnymi rzeczami:
wget oraz jakie mogę być timeouty.Na moim serwerze program uruchamia się co minutę, próbuje połączyć się ze stroną dwa razy, a maksymalny czas oczekiwania na odpowiedź przy każdej z prób wynosi 10 sekund.
Program zapisany jest pod /root/check_apache.sh, a regułka uruchamiania w cronie wygląda następująco:
* * * * * bash /root/check_apache.sh
Przedstawiony problem można rozwiązać na wiele sposobów, przedstawiłem ten najbardziej oczywisty.