dareks_

  • Dokumenty2 821
  • Odsłony705 384
  • Obserwuję401
  • Rozmiar dokumentów32.8 GB
  • Ilość pobrań345 462

SQL. Sztuka Programowania (2007) - Stephane Faroult, Peter Robson

Dodano: 6 lata temu

Informacje o dokumencie

Dodano: 6 lata temu
Rozmiar :21.5 MB
Rozszerzenie:pdf

SQL. Sztuka Programowania (2007) - Stephane Faroult, Peter Robson.pdf

dareks_ EBooki Infornatyka
Użytkownik dareks_ wgrał ten materiał 6 lata temu. Od tego czasu zobaczyło go już 190 osób, 145 z nich pobrało dokument.

Komentarze i opinie (0)

Transkrypt ( 25 z dostępnych 464 stron)

S P I S T R E Ś C I Wstęp 7 1. Plany strategiczne 15 Projektowanie baz danych pod kątem wydajności 2. Prowadzenie wojny 51 Wydajne wykorzystanie baz danych 3. Działania taktyczne 87 Indeksowanie 4. Manewrowanie 113 Projektowanie zapytań SQL 5. Ukształtowanie terenu 151 Zrozumienie implementacji fizycznej 6. Dziewięć zmiennych 179 Rozpoznawanie klasycznych wzorców SQL 7. Odmiany taktyki 231 Obsługa danych strategicznych 8. Strategiczna siła wojskowa 273 Rozpoznawanie trudnych sytuacji i postępowanie w nich 9. Walka na wielu frontach 307 Wykorzystanie współbieżności 10. Gromadzenie sił 337 Obsługa dużych ilości danych 11. Fortele 381 Jak uratować czasy reakcji 12. Zatrudnianie szpiegów 417 Monitorowanie wydajności Ilustracje 451 O autorach 453 Skorowidz 455

Bacząc na moje porady, miej również wzgląd na wszelkie pomocne okoliczności związane z regułami, ale i niezależne od nich. — Sun Tzu, Sztuka wojny

W S T Ę P yły takie czasy, gdy dziedzinę zwaną dziś szumnie „technologią informatyczną” określano „elektronicznym przetwarzaniem danych”. I prawda jest taka, że przy całym szumie wokół modnych technologii przetwarzanie danych nadal pozostało głównym zadaniem większości systemów. Co więcej, rozmiary danych zarządzanych w sposób elektroniczny stale się zwiększają, szybciej nawet niż wzrasta moc procesorów. Najważniejsze dane korporacyjne są dziś przechowywane w relacyjnych bazach danych i można uzyskać do nich dostęp za pomocą niedoskonałego, ale za to powszechnie znanego języka SQL. Jest to kombinacja, która zaczęła przebijać się na światło dzienne w połowie lat osiemdziesiątych i do dnia dzisiejszego praktycznie zmiotła konkurencję z powierzchni ziemi. Trudno dziś przeprowadzić wywiad z młodym programistą, który nie pochwaliłby się dobrą praktyczną znajomością SQL-a, powszechnie stosowanego języka dostępu do danych. Język ten jest jedną z obowiązkowych pozycji każdego kursu wykładów na kierunkach informatycznych. Taka opinia na temat własnej wiedzy jest z reguły względnie uzasadniona, jeśli przez „wiedzę” rozumiemy umiejętność uzyskiwania (po krótszych lub dłuższych zmaganiach) funkcjonalnie poprawnych wyników. Jednakże korporacje na całym świecie doświadczają sytuacji dynamicznie zwiększających się ilości danych. W efekcie „funkcjonalnie poprawne” wyniki już nie wystarczą: wyniki te muszą przede wszystkim być uzyskiwane szybko. Wydajność baz danych stała się głównym problemem w wielu firmach. Co interesujące, choć każdy zgodzi się z faktem, iż źródło wydajności leży w kodzie, akceptuje się fakt, że najważniejszą troską programisty powinno B

8 WSTĘP być pisanie działającego kodu, co wydaje się dość rozsądnym założeniem. Z tego rozumowania wynika twierdzenie, że kod wykonywany przez bazę danych powinien być jak najprostszy, przede wszystkim ze względu na koszt utrzymania, i że „ciężkie przypadki” kodu SQL powinny być rozwiązywane przez zaawansowanych administratorów baz danych, którzy będą w stanie zoptymalizować je do szybszego działania, zapewne z użyciem „magicznych” parametrów bazy danych. Jeśli takie działania nie wystarczą, wszyscy zgadzają się z faktem, że jedynym ratunkiem jest wymiana sprzętu na mocniejszy. Często zdarza się, że tego typu podejście, oparte na zdrowym rozsądku i postawie asekuracyjnej, w ostatecznym rozrachunku okazuje się niezwykle szkodliwe. Pisanie niewydajnego kodu i poleganie na ekspertach, którzy poratują w sytuacji „ciężkiego przypadku” kodu SQL, można porównać do zamiatania brudów pod dywan. W mojej opinii to właśnie programiści powinni interesować się wydajnością, a ich potrzebę znajomości SQL-a oceniam nieco wyżej niż jedynie umiejętność napisania kilku zapytań. Wydajność widziana z perspektywy programisty jest czymś zupełnie innym od „konfigurowania” praktykowanego przez administratorów baz danych. Administrator baz danych próbuje jak najwięcej „wycisnąć” z systemu w oparciu o posiadany sprzęt, procesory i podsystem dyskowy oraz wersję systemu zarządzania bazami danych. Administrator może posiadać pewne umiejętności w używaniu SQL-a i poradzi sobie z poprawieniem wydajności szczególnie kiepsko napisanego zapytania. Ale programiści piszą kod, który będzie działał długo, nawet do pięciu czy dziesięciu lat i wielokrotnie przeżyje bieżącą wersję (niezależnie od ich marketingowych haseł, jak internet-enabled, ready-for-the-grid czy tym podobnych) użytkowanego systemu zarządzania bazami danych (Database Management System, DBMS), na której był pisany, jak również kilka generacji sprzętu. Kod musi być szybki od początku. Przykro to stwierdzić, ale z wielu programistów, którzy „znają” SQL, niewielu rozumie zasady funkcjonowania tego języka i ma opanowane podstawy teorii relacyjnej. Po co kolejna książka o SQL-u? Książki o SQL-u dzielą się na trzy podstawowe typy: książki, które uczą logiki i składni poszczególnych dialektów SQL-a, książki, które uczą zaawansowanych technik i opierają się na rozwiązywaniu problemów,

WSTĘP 9 oraz książki o konfigurowaniu systemów pod kątem wydajności, pisane z myślą o administratorach baz danych. Z jednej strony książki prezentują sposoby pisania kodu SQL. Z drugiej strony, uczą diagnozowania błędnie napisanego kodu SQL. W tej książce starałem się nauczyć bardziej zaawansowanych czytelników pisać prawidłowy kod SQL, a co ważniejsze, uzyskać spojrzenie na kod w SQL-u wykraczające poza pojedyncze zapytania. Nauczenie korzystania z języka programowania samo w sobie jest dość trudnym zadaniem. W jaki sposób nauczyć programowania w tym języku w sposób efektywny? SQL jest językiem sprawiającym wrażenie prostego, ale to złudne wrażenie, co ujawnia się po zagłębieniu się w jego tajniki. Język ten pozwala na wręcz nieskończone ilości kombinacji. Pierwszym porównaniem, jakie mi się narzuciło, była gra w szachy, ale w pewnym momencie uświadomiłem sobie, że gra w szachy została wymyślona po to, by szkolić techniki prowadzenia wojny. Mam naturalną tendencję do traktowania każdego wyzwania jako bitwy z armią wierszy tabel bazy danych i uświadomiłem sobie, że problem uczenia programistów wydajnego wykorzystywania bazy danych jest podobny do problemu uczenia oficerów prowadzenia wojny. Potrzebna jest wiedza, umiejętności i oczywiście potrzebny jest talent. Talentu nie można nauczyć, ale można go pielęgnować. W to wierzyli wielcy dowódcy, od Sun Tzu, który dwadzieścia pięć wieków temu napisał swoją Sztukę wojny, do współczesnych generałów. Swoje doświadczenie starali się przekazać w formie prostych do zrozumienia reguł i maksym, które miały służyć jako punkty odniesienia — jak gwizdy widoczne przez kurzawę i tumult bitew. Chciałem zastosować tę samą technikę na potrzeby bardziej pokojowych celów, zastosowałem plan nauki zbliżony do przyjętego przez Sun Tzu. Od niego zapożyczyłem też tytuł. Wielu uznanych specjalistów informatyki uważa się za naukowców. Jednak do dziedziny wymagającej sprytu, doświadczenia i kreatywności, obok żelaznego rygoru i zrozumienia natury rzeczy1 , moim zdaniem, jednak określenie „sztuka” pasuje bardziej od określenia „nauka”. Liczę się z tym, że moje upodobanie do nazywania tego sztuką będzie źle zrozumiane przez zwolenników nazywania tego nauką, gdyż ci twierdzą, że każdy problem z SQL-em posiada swoje optymalne rozwiązanie, które można osiągnąć drogą skrupulatnej analizy i dzięki dobrej znajomości danych. Jednak osobiście 1 Jedną z moich ulubionych książek informatycznych jest Sztuka programowania Donalda E. Knutha.

10 WSTĘP nie uważam, że te dwa podejścia są wzajemnie sprzeczne. Rygor i podejście naukowe pomagają rozwiązać jeden problem nurtujący użytkownika w konkretnym momencie. W programowaniu w języku SQL, gdzie z reguły nie istnieje niepewność typowa dla pola bitwy przed kolejnym ruchem nieprzyjaciela, największe zagrożenia leżą w ewolucyjnych zmianach. Co się stanie, gdy nieoczekiwanie zwiększy się rozmiar bazy danych? Co będzie, gdy wskutek połączenia z inną firmą liczba użytkowników systemu się podwoi? Co się stanie, jeśli zdecydujemy się udostępniać w systemie dane historyczne pochodzące z kilku lat? Czy program zachowa się w inny sposób niż obecnie? Niektóre wybory w zakresie architektury systemu stanowią pewną formę hazardu. W tym zakresie rzeczywiście potrzebny jest rygor i solidne podłoże teoretyczne, ale te cechy są również kluczowe w przypadku każdej dziedziny sztuki. Ferdynand Foch na swoim odczycie w French Ecole Supérieure de Guerre w roku 1900 (później, w czasie pierwszej wojny światowej, został mianowany dowódcą sił sprzymierzonych) powiedział: Sztuka wojny, jak wszystkie inne dziedziny sztuki, posiada swoją teorię, swoje reguły — w przeciwnym razie nie byłaby sztuką. Ta książka nie jest książką kucharską wyliczającą problemy i zawierającą gotowe „receptury”. Jej celem jest pomoc programistom i ich menedżerom w zadawaniu właściwych pytań. Czytelnik po przeczytaniu i przeanalizowaniu tej książki nie ma żadnej gwarancji, że przestanie pisać błędny i nieoptymalny kod SQL. Czasem zdarza to się każdemu. Ale (mam nadzieję) będzie to robił z pełnym uzasadnieniem i świadomością konsekwencji. Odbiorcy Ta książka jest napisana z myślą następujących odbiorcach: • programistach posiadających konkretne (roczne lub dłuższe) doświadczenie w programowaniu z użyciem baz danych SQL, • ich menedżerach, • architektach zajmujących się projektowaniem programów, w których baza danych stanowi kluczowy element. Choć mam nadzieję, że niektórzy administratorzy baz danych, szczególnie ci, którzy opiekują się rozwojowymi bazami danych, również znajdą w tej książce interesujące dla siebie zagadnienia. Jednak z przykrością stwierdzam, że to nie właśnie ich miałem na myśli.

WSTĘP 11 Założenia przyjęte w tej książce W tej książce przyjąłem założenie, że Czytelnik opanował już język SQL. Przez pojęcie „opanował” nie zakładam, że na studiach zaliczył semestr z języka SQL i otrzymał ocenę celującą ani, że jest uznanym guru od tego języka. Chodzi mi o to, że Czytelnik tej książki powinien mieć doświadczenie z tworzeniem aplikacji wykorzystujących bazy danych z użyciem języka SQL, że miał okazję analizować potrzeby bazy z punktu widzenia indeksowania i że tabeli zawierającej 5 tysięcy wierszy nie uważa za „wielką”. Zadaniem tej książki nie jest objaśnianie zasady działania złączenia (włączenie ze złączeniami zewnętrznymi) ani do czego służą indeksy. Choć nie uważam, żeby konieczna była umiejętność stosowania bardzo zawiłych konstrukcji w SQL-u, to w oparciu o podany zbiór tabel Czytelnik powinien umieć skonstruować funkcjonalnie poprawne zapytanie realizujące określone zadanie. Jeśli tak nie jest, to przed przeczytaniem tej książki polecam kilka innych. Zakładam również, że Czytelnik zna przynajmniej jeden język programowania oraz podstawy technik programowania. Zakładam, że Czytelnik był już w okopach i że słyszał narzekania użytkowników, że „baza danych działa wolno”. Zawartość książki Podobieństwo między wojną a SQL-em uznałem za tak uderzające, że zastosowałem się do schematu opracowanego przez Sun Tzu, pozostawiając też wiele z jego tytułów2 . Ta książka jest podzielona na dwanaście rozdziałów, każdy z nich zawiera definicje pewnych zasad i maksymy. Te zasady starałem się ilustrować przykładami, o ile to możliwe wziętymi z rzeczywistych sytuacji życiowych. Rozdział 1. „Plany strategiczne” Zawiera zasady projektowania baz danych z myślą o wydajności. Rozdział 2. „Prowadzenie wojny” Objaśnia sposoby projektowania programów, aby mogły one korzystać z baz danych w sposób efektywny. Rozdział 3. „Działania taktyczne” Wyjaśnia sposoby użycia indeksów. 2 Kilka tytułów zapożyczyłem od Clausewitza z jego rozprawy Zasady wojny.

12 WSTĘP Rozdział 4. „Manewrowanie” Wyjaśnia sposoby projektowania zapytań SQL. Rozdział 5. „Ukształtowanie terenu” Demonstruje, w jaki sposób implementacja fizyczna może mieć wpływ na wydajność. Rozdział 6. „Dziewięć zmiennych” Omawia klasyczne wzorce sytuacji występujących w zapytaniach SQL oraz sposoby radzenia sobie z nimi. Rozdział 7. „Odmiany taktyki” Zawiera metody obsługi danych hierarchicznych. Rozdział 8. „Strategiczna siła wojskowa” Zawiera porady dotyczące rozpoznawania i obsługi skomplikowanych przypadków. Rozdział 9. „Walka na wielu frontach” Opisuje zasady obsługi współbieżności. Rozdział 10. „Gromadzenie sił” Omawia sposoby przetwarzania dużych ilości danych. Rozdział 11. „Fortele” Oferuje kilka sztuczek, które pomogą przetrwać w sytuacji złych projektów baz danych. Rozdział 12. „Zatrudnianie szpiegów” Stanowi podsumowanie książki przez omówienie wykorzystania mechanizmów monitorowania wydajności. Konwencje zastosowane w książce W książce zostały zastosowane następujące konwencje typograficzne: Pochylenie Wyróżnia nowo wprowadzane terminy oraz tytuły książek. Stała szerokość znaków Używana do wpisywania kodu SQL, a ogólnie: do wyróżnienia słów kluczowych języków programowania, nazw tabel, kolumn i indeksów, funkcji, kodu i wyników wywołania poleceń.

WSTĘP 13 Stała szerokość znaków, pogrubienie Wyróżnia elementy kodu, na które należy zwrócić szczególną uwagę. Stała szerokość znaków, pochylenie Wyróżnia fragment kodu, w którym ma znaleźć się wartość wprowadzona przez użytkownika. Taka ikona sygnalizuje maksymę podsumowującą ważną zasadę dotyczącą SQL-a. UWAGA To jest wskazówka, sugestia lub uwaga ogólna. Zawiera cenne informacje uzupełniające na temat omawianego zagadnienia. Podziękowania Napisanie książki w języku niebędącym Twoim językiem rodzimym ani nawet językiem kraju, w którym mieszkasz, wymaga optymizmu graniczącego z szaleństwem (patrząc na to z perspektywy czasu). Na szczęście Peter Robson, którego spotkałem na kilku konferencjach przy okazji wygłaszania odczytów, wniósł do tej książki nie tylko swoją wiedzę z zakresu języka SQL i zagadnień związanych z bazami danych, lecz również nieograniczony entuzjazm w bezlitosnym skracaniu moich za długich zdań, umieszczaniu przysłówków tam, gdzie ich miejsce czy też sugerowaniu innych słów w zastępstwie tych, które swoją karierę zakończyły w epoce, gdy Anglią rządzili Plantagenetowie3 . Książka została wydana pod redakcją Jonathana Gennicka, autora bestsellerowej pozycji SQL Pocket Guide wydawnictwa O’Reilly i kilku innych znaczących o tej tematyce, co było dla mnie nieco przerażającym zaszczytem. Jak się wkrótce przekonałem, Jonathan jest redaktorem o dużym szacunku dla autorów. Jego profesjonalizm, dbałość o szczegóły i ambitne wyzwania uczyniły tę książkę znacznie lepszą, niż bylibyśmy w stanie uczynić ją sami z Peterem. Jonathan miał też swój środkowoatlantycki wkład w nastrój tej książki (jak się szybko przekonaliśmy z Peretem, ustawienie słownika na English (US) było w tym celu warunkiem koniecznym, ale niewystarczającym). 3 Dla niezorientowanych: Plantagenetowie rządzili Anglią od roku 1154 do roku 1485.

14 WSTĘP Chcę również wyrazić wdzięczność wielu osobom z trzech różnych kontynentów, które znalazły czas, aby przeczytać części lub całość szkicu książki, i udzielały mi swoich szczerych porad: Philippe Bertolino, Rachel Carmichael, Sunil CS, Larry Elkins, Tim Gorman, Jean-Paul Martin, Sanjay Mishra, Anthony Molinaro i Tiong Soo Hua. Szczególny dług wdzięczności odczuwam wobec Larry’ego, ponieważ genezę koncepcji tej książki można z pewnością odkryć w e-mailach, jakie wymieniliśmy między sobą. Chcę również podziękować licznym osobom w Wydawnictwie O’Reilly, które pomogły tej książce przejść do świata realnego. Te osoby to przede wszystkim: Marcia Friedman, Rob Romano, Jamie Peppard, Mike Kohnke, Ron Bilodeau, Jessamyn Read i Andrew Savikas. Wielkie dzięki należą się również Nancy Reinhardt za jej doskonałą redakcję rękopisów. Specjalne podziękowania dla Yann-Arzel Durelle-Marc za użyczenie praw do ryciny ilustrującej rozdział 12. Dziękuję też Paulowi McWhorterowi za pozwolenie na wykorzystanie mapy bitwy, która stała się głównym motywem rysunku z rozdziału 6. Chcę wreszcie podziękować Rogerowi Manserowi i zespołowi Steel Business Briefing za użyczenie Peterowi i mnie biura i ogromnych ilości bardzo potrzebnej kawy podczas londyńskich sesji roboczych, w połowie drogi między naszymi miejscami stałej pracy, oraz Qian Lena (Ashley) za udostępnienie chińskiego tekstu cytatu Sun Tzu, który wykorzystaliśmy na początku książki.

R O Z D Z I A Ł P I E R W S Z Y Plany strategiczne Projektowanie baz danych pod kątem wydajności C’est le premier pas qui, dans toutes les guerres, décèle le génie. W każdej z wojen geniusz poznaje się już po pierwszym ruchu. — Joseph de Maistre (1754 – 1821) List z 27 lipca 1812 roku do Pana Hrabiego z frontu

16 ROZDZIAŁ PIERWSZY ielki niemiecki dziewiętnastowieczny strateg, Clausewitz, twierdził, że wojna jest po prostu kontynuacją polityki, lecz za pomocą innych środków. Analogicznie, każdy program komputerowy jest w jakimś stopniu kontynuacją ogólnych działań w ramach organizacji, pozwalającą wykonać więcej, szybciej, lepiej lub taniej to samo, co da się wykonać innymi środkami. Głównym zadaniem programu komputerowego nie jest pobranie danych z bazy i ich przetworzenie, lecz dokonanie takiego przetworzenia danych, aby został zrealizowany odpowiedni cel. Środki służą jedynie osiągnięciu celu, lecz same w sobie celu nie stanowią. Stwierdzenie, że celem każdego programu komputerowego w pierwszej kolejności jest realizacja określonych wymagań biznesowych1 często bywa traktowane jak komunał. Nierzadko bowiem w wyniku zafascynowania wyzwaniami technologicznymi uwaga użytkowników przesuwa się z celu na środki. Z tego powodu większy nacisk kładziony jest z reguły nie na zachowanie wysokiej jakości danych rejestrujących szczegóły działalności biznesowej, lecz na tworzenie oprogramowania w ustalonym terminie i działającego w ustalony sposób. Przed rozpoczęciem budowania systemu musimy dokładnie określić jego nadrzędne cele, podobnie jak generał dowodzący armią przed rozpoczęciem ofensywy. I powinniśmy się ich trzymać nawet w sytuacji, gdy nieoczekiwane okoliczności (niekorzystne lub korzystne) zmuszą nas do zmiany oryginalnego planu. Wszędzie tam, gdzie będzie wykorzystywany język SQL, musimy walczyć o zachowanie wiarygodnych i spójnych danych przez cały okres ich wykorzystania. Zarówno wiarygodność, jak i spójność danych mają ścisły związek z jakością modelu bazy danych. Model bazy danych, który początkowo stanowił podstawę projektową języka SQL, nosi nazwę modelu relacyjnego. Prawidłowy model danych i odpowiedni dla niego projekt bazy danych to kluczowe aspekty w procesie tworzenia każdego systemu informatycznego. Relacyjny model danych Baza danych to jedynie model niewielkiego wycinka sytuacji z rzeczywistości. I jak każda reprezentacja, baza danych zawsze będzie jedynie modelem nieprecyzyjnym i uproszczonym w porównaniu do rzeczywistości — skomplikowanej i bogatej w szczegóły. Rzadko zdarza się, że jakiejś 1 Określenie wymagania biznesowego jest bardziej ogólne: obejmuje działalność komercyjną oraz niekomercyjną. W

PLANY STRATEGICZNE 17 aktywności biznesowej odpowiada tylko jeden, idealny model reprezentacji danych. Najczęściej do wyboru mamy kilka wariantów, znaczeniowo równorzędnych z technicznego punktu widzenia. Jednak dla pełnego zbioru procesów wykorzystujących dane z reguły daje się wyodrębnić jedną z dostępnych reprezentacji, która najlepiej spełnia wymagania biznesowe. Model relacyjny został nazwany w ten sposób nie dlatego, że za jego pomocą definiuje się relacje (powiązania) między tabelami, lecz dlatego, że poszczególne wartości w kolumnach tabeli występują w ścisłej relacji. Innymi słowy: dla każdego wiersza w tabeli poszczególne wartości kolumn są w relacji. Definicja tabeli polega na określeniu powiązań między kolumnami, a cała tabela to jest właśnie relacja (a dokładniej: każda tabela reprezentuje jedną relację). Wymagania biznesowe określają modelowany zakres sytuacji świata rzeczywistego. Po zdefiniowaniu zakresu należy zidentyfikować dane niezbędne do zarejestrowania działania biznesowego. Jeśli mamy zamodelować bazę dla komisu samochodowego (na przykład na potrzeby reklamy w internecie) będą potrzebne takie dane, jak marka, model, wersja, styl (sedan, coupe, kabriolet itp.), rocznik, przebieg i cena. To są te informacje, które przychodzą na myśl w pierwszej chwili, ale potencjalny klient może zechcieć zapoznać się z bardziej szczegółowymi informacjami, aby dokonać bardziej świadomego wyboru. Na przykład: • ogólny stan pojazdu (nawet w przypadku, gdy z założenia nie będziemy oferować samochodów w stanie innym niż „idealny”), • wyposażenie bezpieczeństwa, • rodzaj skrzyni biegów (manualna lub automatyczna), • kolor (karoseria i wnętrze), farba metaliczna lub nie, tapicerka, szyberdach, być może również zdjęcie samochodu, • maksymalna liczba przewożonych osób, pojemność bagażnika, liczba drzwi, • wspomaganie kierownicy, klimatyzacja, sprzęt audio, • pojemność silnika, liczba cylindrów, moc i prędkość maksymalna, hamulce (nie każdy użytkownik systemu będzie entuzjastą motoryzacji, aby móc określić wszystkie parametry wyłącznie na podstawie typu i wersji), • rodzaj paliwa, zużycie, pojemność zbiornika,

18 ROZDZIAŁ PIERWSZY • aktualna lokalizacja samochodu (może mieć znaczenie w przypadku, gdy firma działa w różnych lokalizacjach), • i wiele innych… Gdy każdy model samochodu odwzorujemy w bazie danych, każdy wiersz tabeli będzie odzwierciedleniem pewnego stwierdzenia faktu z rzeczywistości. Na przykład jeden z wierszy może odzwierciedlać fakt dostępności w komisie różowego Cadillaca Coupe DeVille z roku 1964, który już dwudziestokrotnie przekroczył dystans równy obwodowi Ziemi. Za pomocą operacji relacyjnych, jak złączenia, a także z użyciem filtrowania, wyboru atrybutów lub obliczeń na wartościach atrybutów (na przykład w celu uzyskania informacji na temat średniego dystansu, jaki można pokonać między tankowaniami) możemy uzyskać nowe stwierdzenia faktów. Jeśli oryginalne stwierdzenie było prawdziwe, prawdziwe będą też stwierdzenia uzyskane na jego podstawie. W każdym działaniu związanym z przetwarzaniem wiedzy mamy do czynienia z faktami prawdziwymi, które nie wymagają dowodu. W matematyce takie fakty określa się terminem aksjomatów, lecz ta własność wiedzy nie ogranicza się do matematyki. W innych dziedzinach tego typu prawdy niewymagające dowodu nazywa się pryncypiami. Na bazie tych prawd można tworzyć nowe (w matematyce nazywane twierdzeniami). W ten sposób prawdy mogą służyć jako fundament, w oparciu o który tworzy się nowe prawdy. Relacyjna baza danych działa dokładnie w opisany wyżej sposób. To nie jest bowiem przypadek, że model relacyjny jest ściśle oparty na zjawiskach matematycznych. Definiowane relacje (które, jak już wiemy, w przypadku baz SQL odpowiadają tabelom) reprezentują prawdy przyjmowane a priori jako prawdziwe. Definiowane perspektywy i zapytania stanowią nowe prawdy, które mogą być udowodnione. UWAGA Spójność modelu relacyjnego to koncepcja, która odgrywa bardzo ważną rolę, dlatego warto poświęcić jej odpowiednią ilość czasu i pracy. Pryncypia, na których oparty jest model relacyjny, to reguły matematyczne, sprawdzone i stabilne, dlatego zawsze można liczyć na to, że w wyniku zapytania na prawidłowych danych uzyskamy prawidłowe wyniki. Pod warunkiem jednak, że będziemy ściśle przestrzegać reguł relacyjnych. Jedna z tego typu podstawowych reguł mówi, że relacja, z definicji, nie zawiera duplikatów, a kolejność jej wierszy jest bez znaczenia. Jak dowiemy się w rozdziale 4., język SQL dość swobodnie traktuje teorię relacyjną i tę swobodę można uznać za główną przyczynę powstawania nieoczekiwanych wyników lub problemów optymalizatora z efektywnym wykonywaniem zapytań.

PLANY STRATEGICZNE 19 Dobór prawd podstawowych (czyli relacji) jest dość dowolny. Czasem zdarza się zatem, że ta swoboda prowadzi do błędnych decyzji. Trudno oczekiwać, żeby przy sprzedaży kilku jabłek w budce z warzywami w celu ich zważenia sprzedawca był zmuszony dowodzić prawa Newtona. Podobnie można postrzegać program, który do zupełnie podstawowych operacji wymaga wykonania złączenia dwudziestu pięciu tabel. Firma często wykorzystuje te same dane, co jej dostawcy i klienci. Jednakże jeśli ci dostawcy i klienci nie prowadzą identycznej działalności, postrzeganie tych samych danych będzie nieco inne, dostosowane do potrzeb i sytuacji. Na przykład wymagania biznesowe firmy będą odmienne od analogicznych wymagań jej klientów, nawet mimo tego że obie strony będą wykorzystywać te same dane. Jeden rozmiar nie pasuje wszystkim. Dobry projekt to zatem taki, który nie zmusza do tworzenia karkołomnych zapytań. Modelowanie jest projekcją wymogów biznesowych. Znaczenie normalności Normalizacja, szczególnie w zakresie od pierwszej do trzeciej postaci normalnej (3NF) to elementarne pojęcie teorii relacyjnej, z którym zetknął się na pewno każdy student informatyki. Jak większość rzeczy, których uczymy się w szkole (weźmy na przykład literaturę klasyczną), również normalizacja jest postrzegana jako pojęcie „zakurzone”, nudne i zupełnie oderwane od rzeczywistości. Wiele lat po zakończeniu nauki, gdy znajdzie się czas, aby spojrzeć na problem świeżym okiem wzmocnionym przez doświadczenie, okazuje się, że siła klasycznych pojęć jest wielka i nie poddaje się próbie czasu. Zasada normalizacji jest przykładem zastosowania rygoru logicznego do komponowania danych, które w wyniku normalizacji stają się strukturalizowaną informacją. Ten rygor jest wyrażany w definicjach postaci normalnych, z których najczęściej cytowane są trzy, choć puryści twierdzą, że istnieje postać normalna wykraczająca poza 3NF, której znaczenia nie należy lekceważyć. Tę postać nazywa się postacią Boyce’a-Codda (BCNF), a często

20 ROZDZIAŁ PIERWSZY wręcz piątą postacią normalną (5NF). Bez paniki, my zajmiemy się tylko trzema pierwszymi postaciami. W ogromnej większości przypadków baza danych zaprojektowana w zgodzie z 3NF będzie również zgodna z BCNF2 , czyli 5NF. Dlaczego normalizacja ma znaczenie? Jak sama nazwa wskazuje, normalizacja to proces przekształcania chaosu w porządek. Po bitwie błędy popełnione podczas niej często wydają się oczywiste, a decyzje prowadzące do sukcesu jawią się jako zupełnie naturalne i zgodne ze zdrowym rozsądkiem. Podobnie po przeprowadzeniu normalizacji na danych struktury tabel wyglądają zupełnie naturalnie, a reguły normalizacyjne często określa się (umniejszając ich znaczenie) jako przesadnie wyeksponowane zastosowanie zdrowego rozsądku. Każdy z nas z pewnością wyobraża sobie, że jest wyposażony w odpowiedni zapas zdrowego rozsądku. Jednak w zetknięciu ze skomplikowanymi danymi łatwo się pogubić. Pierwsze trzy postacie normalne są oparte na czystej logice i dzięki temu doskonale sprawdzają się jako kontrolne listy reguł ułatwiające proces wyciągania danych z chaosu. Dość marne są szanse, że w wyniku zastosowania źle zaprojektowanej, nieznormalizowanej bazy danych nasz system ulegnie katastrofie w wyniku uderzenia piorunem, który obróci go w kupkę popiołu, jak na to zasłużył. Dlatego, jak sądzę, tę sytuację należy skatalogować wśród nieudowodnionych teorii. Jednakże takie katastrofy, jak niespójność danych, trudności w projektowaniu elementów interfejsu do wprowadzania danych oraz zwiększone nakłady na rozwiązywanie błędów w nadmiernie skomplikowanych programach to dość prawdopodobne zagrożenia, przy których należy wspomnieć o obniżonej wydajności i trudnościach w rozbudowie modelu. Te zagrożenia, wynikające z niezastosowania się do reguł normalizacji, mają z kolei bardzo wysoki współczynnik prawdopodobieństwa i wkrótce dowiemy się, dlaczego tak właśnie się dzieje. W jaki sposób przekształcić dane z heterogenicznej masy nieustrukturalizowanych informacji w użyteczny model bazy danych? Sama metoda wydaje się dość prosta. Wystarczy zastosować się do kilku zaleceń, które omówimy w kolejnych punktach wraz ze stosownymi przykładami. 2 Tabela jest zgodna z 3NF, ale niezgodna z BCNF, jeśli zawiera różne zbiory nieunikalnych kolumn (kluczy kandydujących, unikalnie identyfikujących identyfikatory w wierszach) posiadających jedną kolumnę wspólną. Tego typu sytuacje nie są bardzo powszechne.

PLANY STRATEGICZNE 21 Etap 1. Zapewnienie atomowości W pierwszym kroku musimy upewnić się, że charakterystyki, czyli atrybuty, danych maja charakter atomowy. Koncepcja atomowości jest dość mało precyzyjna mimo pozornej prostoty swego założenia. Określenie atom pochodzi jeszcze z czasów starożytnej Grecji. Zostało wprowadzone przez Leucippusa, greckiego filozofa, który żył w V wieku przed naszą erą. Atom to coś, co „nie może być podzielone” (jak wiemy, zjawisko rozszczepienia jądra atomowego jest zaprzeczeniem tej definicji). Decyzja o tym, czy dane można uznać za atomowe, to, z grubsza biorąc, problem skali. Na przykład dla generała pułk może być atomową jednostką bojową, lecz zdaniem pułkownika (dowodzącego tym pułkiem) należy go podzielić na bataliony. Podobnie samochód może być jednostką atomową dla handlarza, lecz mechanikowi samochodowemu z pewnością jawi się jako bogactwo elementów i części zamiennych, które stanowią dla niego elementy atomowe. Z czysto praktycznych względów atrybutami atomowymi będziemy nazywać te atrybuty, których wartości mogą w klauzuli WHERE być wykorzystywane w całości. Atrybut można dzielić na składowe w ramach listy SELECT (czyli w definicji zwracanych elementów), lecz jeśli pojawia się potrzeba, aby odwołać się do części wartości atrybutu w ramach klauzuli WHERE, to znak, że dany atrybut pozostawia jeszcze nieco do życzenia z punktu widzenia atomowości. Warto w tym miejscu posłużyć się przykładem. W liście atrybutów na potrzeby systemu obsługi sprzedaży używanych samochodów znajdziemy pozycję „wyposażenie bezpieczeństwa”. To ogólna nazwa, w ramach której mieszczą się różne mechanizmy, jak system hamulcowy ABS, poduszki powietrzne (tylko dla pasażerów, dla pasażerów i kierowcy, przednie, boczne itd.). Do tej kategorii można również zaliczyć mechanizmy zapobiegające kradzieżom, jak blokada skrzyni biegów i centralny zamek. Możemy oczywiście zdefiniować kolumnę o nazwie safety_equipment (wyposażenie bezpieczeństwa), w której będziemy wprowadzać opis słowny zastosowanych zabezpieczeń. Należy jednak mieć świadomość, że stosując opis słowny, tracimy przynajmniej jedną z poważnych zalet baz danych: Możliwość szybkiego wyszukiwania informacji Jeśli klient uzna, że mechanizm ABS jest kluczowy, ponieważ często podróżuje po mokrych, śliskich nawierzchniach, wyszukiwanie wykorzystujące ABS jako główne kryterium będzie działało bardzo

22 ROZDZIAŁ PIERWSZY wolno, ponieważ w celu sprawdzenia, czy podciąg znaków ABS występuje w wartości kolumny safety_equipment (wyposażenie bezpieczeństwa), muszą być odczytane wartości tego atrybutu we wszystkich wierszach tabeli. Jak o tym będę szerzej rozprawiać w rozdziale 3., standardowe indeksy wymagają wartości atomowych (w takim sensie, jaki objaśniłem wyżej) w charakterze kluczy. W niektórych silnikach baz danych można posłużyć się innymi mechanizmami przyspieszającymi działanie zapytań, jak indeksy tekstowe (ang. full text indexing), lecz tego typu mechanizmy z reguły mają wady, na przykład brak możliwości obsługi modyfikacji w czasie rzeczywistym. Wyszukiwanie tekstowe może również generować nieoczekiwane wyniki. Załóżmy, że w tabeli samochodów mamy kolumnę reprezentującą kolor pojazdu w postaci opisu koloru karoserii i wnętrza. Jeśli będziemy wyszukiwać tekstu „niebieski”, ponieważ interesuje nas taki kolor karoserii, zapytanie zwróci również szare samochody z niebieskimi obiciami foteli. Każdy z nas z pewnością doświadczył zalet i wad wyszukiwania tekstowego, w taki sposób działają wszak wyszukiwarki internetowe. Ochrona poprawności danych Wprowadzanie danych to procedura podatna na błędy. Jeżeli przy wyszukiwaniu zostanie wprowadzony tekst ASB, zamiast ABS, baza danych nie jest w stanie zweryfikować poprawności, jeśli wykorzystujemy atrybuty opisowe i wyszukiwanie pełnotekstowe. W takim przypadku użytkownik nie uzyska żadnych wyników bez sugestii o tym, że mógł popełnić błąd literowy. Co się z tym wiąże, niektóre z zapytań będą zwracały nieprawidłowe wyniki — niekompletne, a czasem zupełnie błędne, na przykład w przypadku, gdy zechcemy policzyć wszystkie dostępne samochody wyposażone w ABS. Jeśli chcemy zapewnić poprawność danych, jedynym sposobem (oprócz dokładnego sprawdzania poprawności wprowadzanych danych) jest napisanie skomplikowanej funkcji analizującej ciągi znaków i kontrolującej je przy każdym wprowadzaniu i modyfikacji. Trudno ocenić, co byłoby gorsze: poziom komplikacji takiej funkcji i koszmar związany z jej modyfikacjami i aktualizacją czy obniżenie wydajności przy wprowadzaniu danych. Z drugiej strony, jeśli zamiast ogólnego atrybutu opisowego zastosować atrybut has_ABS o wartościach prawda i fałsz, można by wprowadzić banalną regułę sprawdzającą poprawność danych już na etapie formularza użytkownika.

PLANY STRATEGICZNE 23 Częściowa zmiana ciągu znaków to kolejne zadanie wymagające od programisty wirtuozerii w funkcjach przetwarzania ciągów znaków. Z tych właśnie powodów (i wielu innych) należy unikać pakowania wielu różnych informacji w jeden ciąg znaków. Określanie, czy dane mają już cechy atomowe, to dość trudna sztuka. Najprostszym przykładem są adresy. Czy adres powinien być obsługiwany jako jeden ciąg znaków? A może lepiej zapisywać elementy adresu w osobnych atrybutach? Jeśli zdecydujemy się rozbić adres na kawałki, jak daleko warto się posunąć? Należy mieć na uwadze względność pojęcia atomowości, a przede wszystkim wymagania biznesowe w stosunku do bazy danych. Jeśli na przykład planujemy dokonywać obliczeń lub generować statystyki w rozbiciu na kody pocztowe i miasta, to kod pocztowy i miasto muszą być potraktowane jako wartości atomowe. Należy jednak zachować umiar w rozbijaniu na atomy i utrzymać właściwy poziom rozdrobnienia danych. Podstawowa zasada przy określaniu poziomu rozbicia adresu na składowe polega na dopasowaniu każdego z elementów adresu do reguł biznesowych. Trudno przewidzieć, jakie będą te atrybuty (choć oczywiście w przypadku adresu możliwości są skończone), musimy jednak unikać pokusy stosowania konkretnego schematu atrybutów adresu tylko dlatego, że taki został zastosowany przez inną firmę. Każdy zestaw atrybutów musi być skrupulatnie przetestowany pod kątem konkretnych potrzeb biznesowych. Diabeł tkwi w szczegółach. Popadając w przesadę, można z łatwością otworzyć drzwi wielu potencjalnym problemom. Gdy zdecydujemy się na osobne umieszczanie nazwy ulicy i numeru budynku, co zrobić z firmą ACME Corp, której siedziba mieści się pod adresem „Budynek ACME”? Modelując bazę danych do przechowywania zbędnych informacji, narażamy się po prostu na problemy natury projektowej. Prawidłowe zdefiniowanie poziomu szczegółowości informacji może stanowić kluczowy element mechanizmu przekazywania danych z podsystemu operacyjnego do decyzyjnego. Po zidentyfikowaniu danych atomowych oraz zdefiniowaniu ich wzajemnych powiązań powstają relacje. Następnym etapem jest identyfikacja unikalnej charakterystyki każdego wiersza, czyli klucza głównego. Na tym etapie bardzo prawdopodobne jest, że klucz główny będzie złożony z wielu atrybutów. W naszym przykładzie bazy danych używanych samochodów

24 ROZDZIAŁ PIERWSZY z punktu widzenia klienta samochód jest identyfikowany przez kombinację marki, modelu, stylu, rocznika i przebiegu. Nie ma w tym przypadku znaczenia jego numer rejestracyjny. Nie zawsze prawidłowa definicja klucza głównego jest łatwym zadaniem. Dobrym, klasycznym przykładem analizy atrybutów jest biznesowa definicja „klienta”. Klient może być zidentyfikowany na podstawie nazwiska. Jednak nazwisko nie zawsze jest wystarczającym identyfikatorem. Jeśli klientami mogą być firmy, identyfikacja ich po nazwie może być niewystarczająca. Czy „RSI” to „Relational Software”, „Relational Software Inc” (z kropką po „Inc” lub bez niej, z przecinkiem po „Relational Software” lub bez)? Stosować wielkie litery? Małe? Inicjały wielkimi literami? Istnieje zagrożenie, że dane wpisane do bazy nie będą z niej wydobyte. Wybór nazwy klienta w charakterze identyfikatora to dość ryzykowna decyzja, ponieważ narzuca ona konieczność zachowania określonego standardu zapisu, aby uniknąć wieloznaczności. Zaleca się, by klienci byli identyfikowalni na podstawie standardowej nazwy skróconej lub wręcz unikalnego kodu. Zawsze należy brać pod uwagę zagrożenie wynikające ze zmiany nazwy z Relational Software Inc. na przykład na Oracle Corporation. Jeśli chcemy zachować historię kontaktów z klientem również po zmianie jego nazwy, musimy być w stanie zidentyfikować firmę w dowolnym punkcie czasu niezależnie od jej nazwy. Z drugiej strony zaleca się, żeby, o ile to możliwe, unikać identyfikatorów w postaci nic niemówiących liczb, a korzystać z identyfikatorów czytelnych, posiadających własne, określone znaczenie. Klucz główny powinien charakteryzować dane, co trudno powiedzieć o sekwencyjnym identyfikatorze automatycznie przypisanym każdemu wprowadzanemu wierszowi. Tego typu identyfikator można uzupełnić później, jeśli okaże się, że sztuczny atrybut company_id jest łatwiejszy w utrzymaniu od miejsca założenia i rzeczywistego numeru identyfikacyjnego nadanego firmie przez urząd. Taki sekwencyjny identyfikator można również zastosować w chlubnym charakterze klucza głównego, lecz należy pamiętać, że będzie to forma technicznego substytutu (lub skrótu) pełnoprawnego klucza. Funkcjonuje tu dokładnie ta sama zasada, jak w przypadku stosowania aliasów nazw tabel, gdy w klauzuli filtra można napisać coś takiego: where a.id = b.id Jest to skrócona forma następującego zapisu: where tabela_z_dluga_nazwa.id = tabela_z_nazwa_jeszcze_gorsza_od_poprzedniej.id

PLANY STRATEGICZNE 25 Należy jednak zachować pełną świadomość, że identyfikator liczbowy nie stanowi rzeczywistego klucza głównego i nie należy go z nim mylić. Jeśli wszystkie identyfikatory są atomowe i zostaną zidentyfikowane klucze, dane są zgodne z pierwszą postacią normalną (1NF). Etap 2. Sprawdzanie zależności od klucza głównego Jak zauważyłem wcześniej, część informacji przechowywanych w systemie informatycznym pośrednika handlu używanymi samochodami zapewne będzie znana lepiej zorientowanym entuzjastom motoryzacji. Co więcej, wiele z cech charakterystycznych używanych samochodów nie będzie unikalnych tylko dla jednego egzemplarza. Na przykład wszystkie samochody tej samej marki, modelu, wersji i stylu będą również cechować się taką samą ładownością niezależnie od rocznika i przebiegu. Innymi słowy, w tabeli przechowującej wszystkie informacje o samochodach będziemy mieli sporo informacji zależnych od części klucza głównego. Jakie będą skutki uboczne decyzji, by wszystkie te dane zostały zapisane w jednej tabeli o nazwie used_cars? Nadmiarowość danych Jeśli okaże się, że wystawimy na sprzedaż wiele samochodów tej samej marki, modelu, wersji i stylu (to są z reguły te cechy, które są ogólnie określane jako model samochodu), wszystkie te atrybuty, które nie będą specyficzne dla danego egzemplarza, będą zapisane w bazie wielokrotnie, a dokładniej tyle razy, ile razy wystąpi w niej dany model samochodu. Z tego typu zjawiskiem powtarzalności (nadmiarowości, redundancji) danych wiążą się dwa podstawowe zagrożenia. Po pierwsze, nadmiarowe informacje zwiększają prawdopodobieństwo wprowadzenia do bazy sprzecznych danych z powodu błędów przy wprowadzaniu (co z kolei powoduje, że poprawianie tego typu błędów jest bardzo czasochłonne). Po drugie, nadmiarowe dane to po prostu marnotrawstwo miejsca. Co prawda zasoby dyskowe są coraz mniej kosztowne, co spowodowało, że po prostu przestano obsesyjnie niepokoić się o rozmiar danych. To prawda, ale przy tym zapomina się często, że wraz ze wzrostem pojemności dysków twardych i innych nośników znacznie zwiększa się ilość zapisywanych na nich danych. Do tego dochodzi coraz powszechniejsze zjawisko dublowania danych,

26 ROZDZIAŁ PIERWSZY wykonywania kopii zapasowych na osobnych nośnikach na wypadek awarii oraz na potrzeby tworzenia oprogramowania, gdy po prostu wykorzystuje się kopie rzeczywistych baz danych. W efekcie każdy zmarnowany bajt należy pomnożyć przez pięć i to w najbardziej optymistycznych oszacowaniach. Po obliczeniu zmarnowanych bajtów można uzyskać zupełnie zaskakujące wyniki. Oprócz oczywistego kosztu zasobów sprzętowych należy również uwzględnić koszt przywracania systemu do sprawności po sytuacji awaryjnej. Bywają bowiem sytuacje „nieoczekiwanych przestojów”, na przykład wskutek poważnej awarii sprzętowej, gdy występuje konieczność odtworzenia całej bazy danych z kopii zapasowej. Pomijając inne czynniki, nie da się zaprzeczyć, że dwa razy większa baza danych będzie się odtwarzać dwa razy dłużej. Istnieją instalacje, w których każda minuta przestoju to wymierne, bardzo wysokie koszty. W niektórych środowiskach, jak szpitale, przestój może kosztować nawet zdrowie lub życie. Wydajność operacji Tabela zawierająca dużo informacji (z dużą liczbą kolumn) wymaga więcej czasu na odczyt z dysku i wykonanie pełnego przeszukiwania niż tabela, w której kolumn jest mniej. Jak spróbuję dowieść w kolejnych rozdziałach, pełne przeszukiwanie tabeli to nie tak straszne i niepożądane działanie, jak może się wydawać początkującym programistom. W niektórych przypadkach to absolutnie najlepszy wybór. Jednakże im więcej bajtów znajduje się w każdym z wierszy, tym więcej stron będzie zajmować tabela i tym więcej czasu zajmie jej pełne przeszukiwanie. Gdy zechcemy na przykład wygenerować pełną listę dostępnych modeli samochodów, w przypadku nieznormalizowanej tabeli będziemy zmuszeni wykonać zapytanie SELECT DISTINCT przeszukujące zbiór wszystkich dostępnych samochodów. Zapytanie SELECT DISTINCT nie tylko oznacza przeszukanie całej tabeli samochodów, lecz wiąże się również z koniecznością posortowania wyników w celu eliminacji duplikatów. Gdyby dane wyodrębnić do osobnej tabeli w taki sposób, aby silnik bazy danych mógł przeszukiwać jedynie podzbiór danych, operacja zajęłaby o wiele mniej czasu niż wykonywana na całości. Aby pozbyć się zależności danych od części klucza, należy utworzyć nową tabelę (np. car_model). Klucze tych nowych tabel będą składały się z części klucza naszej tabeli wyjściowej (w tym przypadku marka, model, wersja i styl). Wszystkie atrybuty zależne od tych nowych kluczy należy przenieść

PLANY STRATEGICZNE 27 do utworzonych tabel. W oryginalnej tabeli pozostawiamy tylko atrybuty marka, model, wersja i styl. Ten proces należy powtórzyć również w przypadku silnika i jego cech, które nie są uzależnione od stylu samochodu. Po usunięciu z tabel wszystkich atrybutów uzależnionych od części klucza nasza baza będzie zgodna z drugą postacią normalną (2NF). Etap 3. Sprawdzenie niezależności atrybutów Po przekształceniu wszystkich danych do 2NF można przejść do procesu identyfikacji trzeciej postaci normalnej (3NF). Bardzo często bywa tak, że dane zgodne z 2NF są również zgodne z 3NF, niemniej jednak należy upewnić się co do tego faktu. Wiemy już, że każdy atrybut danych jest zależny wyłącznie od unikalnego klucza. 3NF występuje wówczas, gdy wartości atrybutu nie daje się pozyskać na podstawie wartości innych atrybutów oprócz tych, wchodzących w skład unikalnego klucza. Należy tu odpowiedzieć na pytanie: „Czy znając wartość atrybutu A, jestem w stanie określić wartość atrybutu B?”. Międzynarodowe dane kontaktowe stanowią doskonały przykład sytuacji, gdzie atrybut jest zależny od innego atrybutu niebędącego kluczem. Gdy znamy kraj, nie mamy potrzeby zapisywać w numerze telefonu międzynarodowego numeru kierunkowego. Jednak odwrotna sytuacja już nie zachodzi; na przykład zarówno Kanada, jak i USA mają ten sam międzynarodowy numer kierunkowy. Jeśli w bazie potrzebne są obydwa rodzaje informacji, z adresem można na przykład zapisać kod ISO kraju (dla Polski będzie to PL), a w osobnej tabeli country_info z kodem kraju służącym jako klucz główny zapisać wszystkie istotne informacje związane z danymi adresowymi w danym kraju. W tabeli country_info można na przykład zapisać informację o numerze kierunkowym kraju (48 w przypadku Polski), walucie itp. Każda para atrybutów w danych zgodnych z 2NF powinna być przeanalizowana pod kątem tego typu powiązań. To sprawdzenie jest z reguły żmudnym procesem, lecz kluczowym, jeśli dane mają być naprawdę zgodne z 3NF. Jakie jest ryzyko związane z posiadaniem danych niezgodnych z 3NF? Praktycznie analogiczne do tego, co grozi w przypadku niezgodności z 2NF. Istnieje kilka powodów, dla których modelowanie danych w zgodzie z 3NF jest istotne. Istnieje też kilka powodów, dla których modelowane bazy danych celowo nie są zgodne z 3NF. Należy do nich modelowanie wymiarowe,

28 ROZDZIAŁ PIERWSZY o którym pokrótce wspomnę w rozdziale 10. Lecz zanim zaczniemy celowo podważać regułę, warto znać powody jej stosowania i ryzyko związane z jej lekceważeniem. Prawidłowy znormalizowany model chroni przed zagrożeniami w wyniku ewolucji wymagań W rozdziale 10. omówię dokładnie powody, dla których stosowane są odstępstwa od normalizacji, jak na przykład modelowanie wymiarowe. Dość wspomnieć, że tego typu modele wymagają przyjęcia mnóstwa założeń dotyczących obsługi danych i zapytań. To samo można powiedzieć o fizycznych strukturach danych, które omówię w rozdziale 5. Jednak istnieje tu ważna różnica: modyfikacje w implementacjach fizycznych nie niszczą całej koncepcji, jak ma to miejsce w implementacjach modeli wymiarowych, mogą jednak mieć poważny wpływ na wydajność działań w bazie. Jeśli pewnego dnia okaże się, że przyjęte założenia dotyczące modelu wymiarowego były nieprawidłowe, jedyne rozwiązanie tej sytuacji polega na „wyrzuceniu” modelu i rozpoczęciu pracy od nowa. W przypadku danych zgodnych z 3NF model wymaga jedynie pewnych modyfikacji zapytań, lecz jest wystarczająco elastyczny, aby przystosować się do wielu typów modyfikacji logicznej interpretacji danych. Normalizacja minimalizuje duplikację danych Jak już wspominałem, duplikacja danych jest zjawiskiem kosztownym, zarówno z powodu zajętości dysków twardych, jak i mocy procesora, ale przede wszystkim prowadzi do zwiększenia prawdopodobieństwa uszkodzenia danych. Takie uszkodzenie występuje w przypadku, gdy modyfikowana jest wartość danych w jednym miejscu, lecz identyczna wartość w innym miejscu bazy nie zostaje odpowiednio (jednocześnie) zmodyfikowana. Utrata informacji nie musi zatem wiązać się z usunięciem danych: jeśli jedna część bazy danych mówi, że kolor jest „biały”, a druga, że ten sam kolor jest „czarny”, mamy do czynienia z utratą informacji. Niespójności danych można częściowo zapobiec dzięki mechanizmom wbudowanym w system DBMS, o ile pozwala na to zastosowany model. W definicji danych można bowiem zawrzeć więzy integralności i ograniczenia atrybutów. Jeśli dane nie są odpowiednio zaprojektowane, można próbować wykorzystać dodatkowe zabezpieczenia programowe. Mamy bowiem możliwość wykorzystania wyzwalaczy i procedur osadzonych. Takie mechanizmy mają jednak tendencje do