PostgreSQL jest obok MySQL jedną z najpopularniejszych, darmowych baz danych. Baza ta oferuje bardzo ciekawe funkcjonalności, jak wspomniane w tytule dziedziczenie. Dziedziczenie odnosi się do tabel i umożliwia na stworzenie rozbudowanej struktury danych bez konieczności tworzenia ogromnej ilości relacji lub nadmiarowych kolumn.

Dziedziczenie w PostgreSQL  jest zbliżone w założeniach do dziedziczenia obecnego w programowaniu obiektowym. W praktyce wygląda to w ten sposób, że tabela potomka rozszerza definicję tabeli rodzica o własne kolumny. Zabieg ten znacznie ułatwia tworzenie zależnych od siebie struktur danych bez konieczności pisania triggerów, czy kodu po stronie serwera aplikacji.

A jak to wygląda w praktyce?

Zasadę działania dziedziczenia pokażę na przykładzie prostej aplikacji kalendarza, w którym można zapisywać różne zdarzenia (event-y). Każde takie zdarzenie ma konkretny typ, nazwę oraz datę wystąpienia. W zależności od typu zdarzenia, istnieje możliwość zdefiniowania dodatkowych parametrów, np. flaga określająca, czy wysłać przypomnienie lub szerszy opis zdarzenia.

W “klasycznym” podejściu do tego problemu, stworzona zostałaby jedna tabela, zawierająca kolumnę, która odpowiedzialna byłaby za identyfikację każdego wiersza. Poza tym każdy wiersz posiadałby nadmiarowe kolumny, co stanowiłoby dodatkowy problem w przypadku kolumn NOT NULL lub z wymaganym konkretnym typem danych.

Jeśli zastosujemy dziedziczenie nasza baza danych będzie miała następującą postać:

CREATE TABLE tabevent
(
  id serial NOT NULL,
  sname character varying(250) NOT NULL,
  tdate timestamp with time zone NOT NULL DEFAULT now(),
  CONSTRAINT tabevent_pkey PRIMARY KEY (id)
)
WITH (
  OIDS=TRUE
);

CREATE TABLE tabnote
(
  scontent text NOT NULL
)
INHERITS (tabevent)
WITH (
  OIDS=TRUE
);

CREATE TABLE tabappointment
(
  splace character varying(1000) NOT NULL
)
INHERITS (tabevent)
WITH (
  OIDS=TRUE
);

CREATE TABLE tabreminder
(
  tremind timestamp with time zone NOT NULL DEFAULT now(),
  bsendmail boolean DEFAULT false
)
INHERITS (tabevent)
WITH (
  OIDS=TRUE
);

Dodając wiersze do tabeli tabnote, tabappointment lub tabreminder, zaobserwujemy, że tabevent również zapełnia się danymi. Oczywiście do tabevent zapisują się tylko te kolumny, które są w tej tabeli zdefiniowane. Do tabeli rodzica również można dodać nowe wiersze. Należy jednak pamiętać, że nie zostaną one wyświetlone w żadnej z potomnych tabel.

Zapytania  SQL na tabelach korzystających z dziedziczenia wykonuje się w taki sam sposób jak w przypadku normalnych tabel. Dodatkowo do dyspozycji mamy słowo kluczowe ONLY. Użyte w zapytaniu na tabeli rodzica odniesie się tylko do tych wierszy, które zostały dodane bezpośrednio do rodzica.

SELECT * FROM ONLY tabevent

Nie ma róży bez kolców

Niestety rozwiązanie to nie jest pozbawione wad. Pierwszą z nich to, że baza wykorzystująca dziedziczenie jest nieprzenaszalna na inne silniki bazodanowe. Kolejną wadą jest to, że tylko ograniczenia NOT NULL są dziedziczone z tabeli rodzica. Wszystkie inne ograniczenia, w tym klucze podstawowe i obce, oraz klucze unikalności, nie są. Podobnie rzecz się ma z prawami dostępu do tabel.

Sądzę, że mimo tych dosyć poważnych wad, warto używać dziedziczenia. Oczywiście należy robić to z głową, by nie powstały bazodanowe potworki.