Zależności funkcyjne c.d. Przykłady. Relacja Film (zapis w postaci tabeli): Tytuł Rok Długość typfilmu nazwastudia nazwiskogwiazdy Gwiezdne 1977 124 Kolor Fox Carrie Fisher Gwiezdne 1977 124 Kolor Fox Mark Hamill Gwiezdne 1977 124 Kolor Fox Harrison Ford Potęzne 1991 104 Kolor Disney Emilio Estevez Kaczory Świat 1992 95 Kolor Paramount Dana Carvey Świat 1992 95 Kolor Paramount Mike Meyers Zależności funkcyjne {tytuł, rok} {długość} {tytuł, rok} {typfilmu} {tytuł, rok} {nazwastudia} W książkach można spotkać uproszczony zapis zależności funkcyjnej: tytuł, rok długość lub tytuł rok długość Taka konwencja będzie czasem wykorzystywana. Kluczem kandydującym w tej realizacji relacji (ekstensji) jest {tytuł, rok, nazwiskogwiazdy}. Możemy założyć, że taki klucz kandydujący jest we wszystkich dopuszczalnych (legalnych) realizacjach (stanach) relacji. Przy takim założeniu dopuszczamy możliwość wyprodukowania w różnych latach dwóch filmów o takich samych tytułach i z udziałem tych samych gwiazd. Nie dopuszczamy możliwości wyprodukowania w tym samym roku dwóch różnych filmów o takich samych tytułach (i ew. z udziałem tej samej gwiazdy). W wielu przypadkach w relacji można wskazać więcej niż jeden klucz kandydujący. Jeden z kluczy kandydujących relacji jest wybierany jako klucz główny. Wybór klucza głównego w konkretnym systemie może mieć wpływ na sposób przechowywania danych na przykład tworzone są indeksy, dane mogą być układane w kolejności wynikającej z posortowania wartości klucza. Nadkluczem (superkluczem, zbiorem identyfikującym) nazywa się zbiór atrybutów, który zawiera klucz kandydujący.
Zależności funkcyjne trywialne Niech A = {A 1, A 2,..., A n }, B = {B 1, B 2,..., B m } Mówimy, że zależność A B jest a) trywialna, jeśli zbiór atrybutów B jest podzbiorem zbioru A, b) nietrywialna, jeśli co najmniej jeden atrybut ze zbioru B nie należy do zbioru A, c) całkowicie nietrywialna, jeśli przecięcie A i B jest zbiorem pustym. Uwagi. W wielu książkach zależność funkcyjna definiowana jest jako zależność pojedynczego atrybutu od zbioru atrybutów. Zapis {A 1, A 2,..., A n } {B 1, B 2,..., B m } (lub krótko A B) oznacza wówczas zbiór zależności funkcyjnych pojedynczych atrybutów B 1, B 2,..., B m od tego samego zbioru atrybutów {A 1, A 2,..., A n }. Nietrywialna zależność funkcyjna (traktowana jako zależność pojedynczego atrybutu od zbioru atrybutów) jest wówczas definiowana jako zależność, w której atrybut z prawej strony nie należy do zbioru atrybutów z lewej strony. Zbiór atrybutów z lewej strony zależności funkcyjnej bywa nazywany zbiorem lewostronnym. Uwagi dotyczące NULL Date krytykuje wprowadzenie pojęcia NULL i logiki trójwartościowej do teorii relacyjnych baz danych. Zamiast NULL proponuje wykorzystać wartość domyślną. Zadanie domowe: rozdziały 4, 5 i 20 z książki Date: Wprowadzenie do systemów baz danych. W poniższych rozważaniach zakłada się, że NULL nie jest stosowany.
Projektowanie relacyjnych schematów baz danych. Problemy, które mogą wystąpić, jeśli dane w pojedynczej relacji powtarzają się (występuje redundancja) 1) problem niespójności 2) anomalie modyfikacji, 3) anomalie dodawania, 4) anomalie usuwania. Przykład Nazwisko Imię PESEL Adres Nr Tytuł Cena Data Termin Data kasety wypożyczenia zwrotu zwrotu Kowalski Jan 123 Zielona 3 5 Pan Tadeusz 5zł 15-05- 16-05- 16-05- Kowalski Jan 123 Zielona 3 17 Quo vadis 5zł 15-05- 16-05- 16-05- Nowak Paweł 311 Zdrojowa 10 Szczęki 2 zł 15-05- 18-05- 18-05- 15/3 Nowak Paweł 311 Zdrojowa 19 Bajki 1 zł 15-05- 18-05- 18-05- 15/3 cz.15........................ Kowalski Jan 123 Zielona 3 10 Szczęki 2 zł 21-05- 22-05- 22-05-........................ Kowalski Jan 123 Zielona 3 5 Pan Tadeusz 5 zł 12-07- 13-07- 13-07- Nowak Paweł 311 Zdrojowa 127 Szczęki 2 zł 12-07- 13-07- 13-07- 15/3 Dekompozycja relacji. Sposobem na eliminację powyższych problemów jest dekompozycja relacji, czyli podział relacji na mniejsze relacje. Sposób dekompozycji relacji na dwie relacje. Niech relacja R ma zbiór atrybutów (schemat) {A 1,..., A n }. Relację R dekomponujemy na relację S o schemacie {B 1,..., B m } oraz relację T o schemacie {C 1,..., C k } tak, by spełnione były następujące zasady: 1. {A 1,..., A n } = {B 1,..., B m } {C 1,..., C k }. 2. Krotki relacji S powstają przez rzutowanie wszystkich krotek relacji R na zbiór atrybutów {B 1,..., B m }. 3. Analogicznie krotki relacji T powstają przez rzutowanie wszystkich krotek relacji R na zbiór atrybutów {C 1,..., C k }. Analogicznie można określić dekompozycję relacji na więcej niż dwie relacje.
Można mówić o dekompozycji samych schematów. Dekompozycja schematu oznacza podział schematu na podzbiory. Przykład: Dekompozycja relacji Film: Tytuł Rok Długość typfilmu nazwastudia nazwiskogwiazdy Gwiezdne 1977 124 Kolor Fox Carrie Fisher Gwiezdne 1977 124 Kolor Fox Mark Hamill Gwiezdne 1977 124 Kolor Fox Harrison Ford Potęzne 1991 104 Kolor Disney Emilio Estevez Kaczory Świat 1992 95 Kolor Paramount Dana Carvey Świat 1992 95 Kolor Paramount Mike Meyers Po dekompozycji na relacje o schematach { tytuł, rok, długość, typfilmu, nazwastudia } oraz { tytuł, rok, nazwiskogwiazdy } otrzymujemy: Relacja Film1: tytuł Rok długość typfilmu nazwastudia Gwiezdne 1977 124 kolor Fox Potęzne 1991 104 kolor Disney Kaczory Świat 1992 95 kolor Paramount Relacja Film2: tytuł Rok nazwiskogwiazdy Gwiezdne 1977 Carrie Fisher Gwiezdne 1977 Mark Hamill Gwiezdne 1977 Harrison Ford Potęzne 1991 Emilio Estevez Kaczory Świat 1992 Dana Carvey Świat 1992 Mike Meyers
Dzięki zdekomponowaniu relacji Film na dwie relacje Film1 i Film2 usunięte zostało źródło anomalii (redundancja). Odzyskiwanie danych po dekompozycji Chcemy, by w wyniku dekompozycji była możliwość takiego połączenia krotek powstałych w wyniku rzutowania, aby uzyskany zbiór krotek zawierał wszystkie i tylko te krotki, które należały do relacji przed dekompozycją. Twierdzenie Heatha: Niech X, Y, Z będą niepustymi zbiorami atrybutów, niech schemat relacji R będzie sumą tych zbiorów. Jeśli w R jest spełniona zależność funkcyjna X Y, wówczas relacja jest równa złączeniu (naturalnemu) swoich rzutów na X Y oraz X Z (w niektórych książkach można w tym kontekście spotkać zapis {X,Y} w znaczeniu sumy zbiorów X i Y). I.J. Heath. Unacceptable File Operations in a Relational Database. Proc. 1971 ACM SIGFIDET Workshop on Data Description, Access and Control, San Diego, California (November 1971). Przykład Relacja o schemacie {A,B,C} z kluczem kandydującym A oraz zależnościami funkcyjnymi A B i B C. A B C 1 a 10 2 b 12 3 a 10 4 c 15 Po dekompozycji otrzymujemy relacje: A B 1 a 2 b 3 a 4 c B C a 10 b 12 c 15 Jeśli dekompozycja nie wiąże się z wykorzystaniem zależności funkcyjnych, to może się zdarzyć, że przez złączenie naturalne nie odtworzymy relacji oryginalnej. Na przykład, gdyby w powyższym przypadku nie było zależności B C:
A B C 1 a 10 2 b 12 3 a 20 4 c 15 Po rzutowaniu mamy A B 1 a 2 b 3 a 4 c B C a 10 b 12 a 20 c 15 Przy próbie odtworzenia relacji oryginalnej mamy: A B C 1 a 10 1 a 20 2 b 12 3 a 10 3 a 20 4 c 15 Postać normalna Boyce a-codda PNBC, BCNF (Boyce-Codd Normal Form) Relacja jest w postaci normalnej Boyce a Codda wtedy i tylko wtedy, gdy dla każdej nietrywialnej zależności funkcyjnej {A 1,...,A n } B (B jest tu pojedynczym atrybutem) zbiór {A 1,..., A n } jest nadkluczem relacji. Przykład: Rozważana wcześniej relacja Film nie jest w postaci BCNF. Kluczem jest zbiór { tytuł, rok, nazwiskogwiazdy }. W relacji występuje zależność funkcyjna {tytuł, rok} {długość, typ filmu, nazwastudia} Lewa strona tej zależności nie jest nadkluczem.
Propozycja (podana jako twierdzenie w [Widom, Ullman ]), mająca uzasadnić poprawność algorytmu doprowadzania relacji do postaci BCNF: Każda relacja binarna (tzn. o dwóch atrybutach) jest w postaci BCNF. Próba uzasadnienia: Jeśli zachodzą tylko zależności {A,B} {A}, {A,B} {B} to ok. Jeśli zachodzi tylko zależność {A} {B}, to jedynym kluczem kandydującym jest {A}. Jeśli zachodzi tylko zależność {B} {A}, to jedynym kluczem kandydującym jest {B}. Jeśli zachodzą zależności {A} {B} i {B} {A} to kluczami kandydującymi są {A} i {B}. Powyższe uzasadnienie nie obejmuje jednak możliwego ( patologicznego ) przypadku, kiedy lewa strona zależności jest zbiorem pustym. Date podaje przykład binarnej relacji USA o dwóch atrybutach COUNTRY i STATE. We wszystkich krotkach atrybut COUNTRY przyjmuje wartość USA, zatem mamy zależność funkcyjną {} {COUNTRY}. Kluczem jest {STATE}, zatem relacja nie jest w PNBC. Zainteresowanych odsyłam do rozdziału 5 w [Date, Wprowadzenie do systemów baz danych, WNT ], w szczególności do rozwiązania zadania 5.7 (i zadania 4.5 z rozdziału 4). Dekompozycja do postaci BCNF Każdą relację można zdekomponować w sposób bezstratny na relacje w postaci BCNF, tzn. tak, że istnieje możliwość dokładnego odtworzenia (przez złączenie naturalne) pierwotnej relacji z relacji powstałych po dekompozycji. Strategia bezstratnej dekompozycji do BCNF: Szukamy wszystkich nietrywialnych, pełnych zależności funkcyjnych {A 1,...,A n } {B i }, (B i jest tu pojedynczym atrybutem), które naruszają warunek BCNF, tzn. {A 1,...,A n } nie jest nadkluczem. Załóżmy, że otrzymamy zależność (*) {A 1,...,A n } {B 1,...,B m }. Dzielimy schemat relacji na dwa nierozłączne podzbiory: jeden zawierający wszystkie atrybuty występujące w zależności (*) naruszającej BCNF, drugi zawierający atrybuty z lewej strony rozważanej zależności (*) oraz atrybuty nie występujące ani z lewej ani z prawej strony tej zależności. Strategię stosujemy do relacji powstałych w wyniku dekompozycji do chwili, gdy wszystkie powstałe relacje są w BCNF. Przykład. Dla rozważanej relacji Film mamy zależność funkcyjną: {tytuł, rok} {długość, typfilmu, nazwastudia} Dekomponujemy relację Film na: { tytuł, rok, długość, typfilmu, nazwastudia } oraz { tytuł, rok, nazwiskogwiazdy }
Normalizacja doprowadzenie relacji do postaci BCNF, przykłady Przykład 1 Rozważmy relację: tytuł Rok długość typ filmu nazwastudia adresstudia Gwiezdne 1977 124 kolor Fox Hollywood Potęzne 1991 104 kolor Disney Buena Vista Kaczory Świat 1992 95 kolor Paramount Hollywood Rodzina Adamsów 1991 102 kolor Paramount Hollywood Kluczem jest { tytuł, rok } Jednocześnie jest zależność funkcyjna: {nazwastudia} {adresstudia} Dekomponujemy schemat relacji na dwa zbiory: { nazwastudia, adresstudia } oraz { nazwastudia, tytuł, rok, długość, typfilmu } Przykład 2 Należy zaprojektować tabele do bazy danych, która służy do ewidencjonowania sprzedaży i wystawiania faktur. Punktem wyjściowym zadania może być faktura, zawierająca następujące pozycje: Nr_faktury (np. 123/12/09), Data_wystawienia, Data_sprzedaży, Nazwa_odbiorcy, Adres_odbiorcy, Wystawiający, Lista_towarów (Nazwa_towaru, Jednostka, Cena_netto, %VAT, Cena_brutto, Ilość, Wartość_zakupu), Wartość_łączna_zakupów. Można dodać inne pozycje. Dla uproszczenia możemy założyć, że cechy obiektów, o których przechowujemy informacje nie zmieniają się w czasie, a więc np. nazwy i adresy odbiorców są niezmienne, ceny netto towarów są niezmienne, nie zmienia się VAT na towar. Pola, których wartość można obliczyć na podstawie pewnego wzoru (wykorzystującego ew. wartości z innych pól w wierszu) nie powinny być przechowywane w tabeli. Uwaga: w praktyce, ze względów wydajnościowych czasem wykorzystuje się takie pola, można na nich nawet budować indeksy (indeksom poświęcony będzie osobny wykład). Na wykładzie przedstawione zostały kroki procedury normalizacji (doprowadzenia do BCNF). Przy pewnych założeniach odnośnie zależności funkcyjnych otrzymaliśmy cztery tabele (nazwy pól kluczy głównych są pogrubione): Faktury: {Nr_faktury, Data_wystawienia, Data sprzedaży, Wystawiający, Nazwa_odbiorcy}
Odbiorcy: {Nazwa_odbiorcy, Adres_odbiorcy} Towary: {Nazwa_towaru, Kategoria_towaru, Jednostka, Cena_netto} Kategorie: {Kategoria_towaru, %VAT} Szczegóły_faktur: {Nr_faktury, Nazwa_towaru, Ilość} Ze względów wydajnościowych można wprowadzić pola ID_odbiorcy, ID_towaru i ID_kategorii: Faktury: {Nr_faktury, Data_wystawienia, Data sprzedaży, Wystawiający, Id_odbiorcy} Odbiorcy: {Id_odbiorcy, Nazwa_odbiorcy, Adres_odbiorcy} Towary: {Id_towaru, Nazwa_towaru, Id_kategorii, Jednostka, Cena_netto} Kategorie: {Id_kategorii, Nazwa_kategorii, %VAT} Szczegóły_faktur: {Nr_faktury, Id_towaru, Ilość} Problem zmieniających się w czasie wartości atrybutów. Powyżej przyjęliśmy, że pewne wartości nie zmieniają się w czasie. Najpierw problem zmieniających się w czasie wartości prześledziliśmy na przykładzie zmieniających się cen. Rozwiązanie 1 Zamiast zależności funkcyjnej {Nazwa_towaru} {Cena_netto} można założyć zależność: {Nazwa_towaru, Data} {Cena_netto} i przeprowadzić normalizację zgodnie z podanym algorytmem. Inne zaprezentowane niżej rozwiązania polegają na zastosowaniu pewnych wzorców po normalizacji przeprowadzonej z założeniem niezmiennych w czasie cen. Rozwiązanie 2 Do tabeli Towary należałoby dodać pole Od_kiedy (od kiedy obowiązuje dana cena). Kluczem kandydującym nie będzie już {Nazwa_towaru}, tylko {Nazwa_towaru, Od_kiedy}. Wobec faktu, że jest zależność funkcyjna {Nazwa_towaru} {Kategoria_towaru, Jednostka} tabela Towary nie jest w postaci BCNF. Należy dokonać dekompozycji na dwie tabele jedna Towary z polami {Nazwa_towaru, Kategoria_towaru, Jednostka}, druga Ceny, z polami {Nazwa_towaru, Od_kiedy, Cena}. Z punktu widzenia wydajności (i z punktu widzenia wygody programistów baz danych) nie jest to najlepsze rozwiązanie. Zdania SQL, które wyświetlałyby wszystkie dane dotyczące faktur byłyby stosunkowo skomplikowane. Zadanie domowe: Proponuję napisać zdanie SQL, które wybierze wszystkie wiersze ze wszystkich tabel z rozwiązania 2 tak, by wyświetlić wszystkie dane szczegółowe faktur. Rozwiązanie 3 Rozwiązanie to jest nieznaczną modyfikacją rozwiązania 2. Do tabeli Ceny dodajemy pole Do_kiedy, zawierające informację do kiedy dana cena obowiązuje (przy założeniu, że ceny nie zmieniają się w środku dnia sprzedaży, w przeciwnym przypadku można wprowadzić datę z godziną). Dla ceny aktualnie obowiązującej to pole byłoby puste (lub zawierałoby pewną
wartość domyślną). Taka prosta modyfikacja znacznie upraszcza zdania SQL, odwołujące się do ceny sprzedaży towaru. Rozwiązanie 4 W rozwiązaniu tym nie będziemy modyfikować tabeli Towary (powstałej przy normalizacji zakładającej niezmienność cen), jednak założymy, że będziemy przechowywać informacje tylko o bieżącej cenie. To oczywiście nie wystarcza. Gdybyśmy na tym poprzestali, to w momencie zmiany ceny pewnego towaru informacja o sprzedaży tego towaru z poprzednich dni nie byłaby poprawna (szczegółowe zestawienie wszystkich danych z faktur zawierałoby niepoprawne kwoty). Należy dodać pole Cena_bieżąca to tabeli Szczegóły_faktur. Zaletą tego rozwiązania jest prostota w naszym przypadku tabela Towary nie musi być dzielona na kilka, zatem wszystkie zdania SQL odwołujące się do Ceny są stosunkowo proste. Wada: informacje o poprzednich cenach towarów są tylko w tabeli Szczegóły_faktur, zatem jeśli jakiś towar nie był w pewnym okresie sprzedawany, to nie mamy z tego okresu informacji o cenach. Czasem takie informacje mogą być z jakiegoś powodu potrzebne (np. do różnych analiz). Inne rozwiązania Różne połączenia poprzednich rozwiązań.