Datenbanken und Prolog



Tabellen in Prolog

Wir betrachten die folgenden beiden Tabellen:

Tablle "t":

a b c
1 2 3
4 5 6
Tablle "s":
c d
3 7
6 8
Die Tabellen kann man in Prolog folgendermaßen repräsentieren:
t(1, 2, 3).
t(4, 5, 6).

s(3, 7).
s(6, 8).

Einfache Projektion

Folgende Regel selektiert die b-Spalte der Tabelle "t":
select_value_from_t(Value) :-
   t(_, Value, _).

?- select_from_t(Value).

Value = 2 ;
Value = 5

Yes

select_all_values_from_t(Values) :-
   findall( Value,
      select_value_from_t(Value),
      Values ).

?- select_all_values_from_t(Values).

Values = [2, 5]

Yes
Um mittels der Attributnamen selektieren zu können muß man auch die Tabellenstrukturen in Prolog repräsentieren:
attribute(t, 1, a).
attribute(t, 2, b).
attribute(t, 3, c).

attribute(s, 1, c).
attribute(s, 2, d).
Dann kann man etwas allgemeiner selektieren:
select_value_from_t(Attribute, Value) :-
   attribute(Table, N, Attribute), 
   t(X, Y, Z),
   nth(N, [X, Y, Z], Value).

?- select_value_from_table(t, a, Value).

Value = 1 ;
Value = 4

Yes
Man beachte, daß für jede Tabelle eine eigenes Prädikat select_value_from_Tabelle erforderlich ist.

Allgemeine Projektion

Wenn man allgemeine Tabellen generisch bearbeiten möchte, so kann man wie folgt vorgehen. Wir ändern die Repräsentation der Tabellen etwas:
t([1, 2, 3]).
t([4, 5, 6]).

s([3, 7]).
s([6, 8]).
Da man nicht einfach "Table(Tuple)" aufrufen kann, wird zuerst mittels Goal =.. [Table, Tuple] ein neues Atom "Table(Tuple)" konstruiert und der Variablen Goal zugewiesen. Dieses wird dann mittels "call(Goal)" aufgerufen.
select_value_from_table(Table, Attribute, Value) :-
   attribute(Table, N, Attribute), 
   Goal =.. [Table, Tuple],
   call(Goal),
   nth(N, Tuple, Value).

select_all_values_from_table(Table, Attribute, Values) :-
   findall( Value,
      select_value_from_table(Table, Attribute, Value), 
      Values ).

?- select_all_values_from_table(t, a, Values).

Values = [1, 4]

Yes
Folgende Regel selektiert Paare aus Attributen und Werten:
select_all_values_from_table(Table, AVs) :-
   findall( Attribute:Value,
      select_value_from_table(Table, Attribute, Value), 
      Values ).

?- select_all_values_from_table(t, AVs).

AVs = [a:1, b:2, c:3, a:4, b:5, c:6]

Yes

Selektion

Die Selektion aller Tupel aus einer Tabelle, die einen speziellen Attributwert haben, kann wie folgt erfolgen:
select_tuple_with_value(Table, Attribute, Value, Tuple) :-
   attribute(Table, N, Attribute),
   Goal =.. [Table, Tuple],
   call(Goal),
   nth(N, Xs, Value).

select_all_tuples_with_value(Table, Attribute, Value, Tuples) :-
   findall( Tuple,
      select_tuple_with_value(Table, Attribute, Value, Tuple),
      Tuples ).

?- select_all_tuples_with_value(t, a, 1, Tuples).

Tuples = [[1, 2, 3]]

Yes
Das Ergebnis ist in diesem Falles eine Liste [[1, 2, 3]], die genau ein Tupel [1, 2, 3] enthält.

Aggregation

Die Summe aller Werte zu einem gegebenen Attribut kann wie folgt berechnet werden:
sum_of_values(Table, Attribute, Sum) :-
   findall( Value,
      select_value_from_table(Table, Attribute, Value),
      Values ),
   add(Values, Sum).   

?- sum_of_values(s, d, Sum).

Sum = 15

Yes

Join

Der Join aus "s" und "t" über gleiche Attributwerte für "c" kann wie folgt berechnet werden:
join_of_s_and_t(Tuples) :-
   findall( [A,B,C,D],
      ( t(A,B,C),
        s(C,D) ),
      Tuples ).

?- join_of_s_and_t(Tuples).

Tuples = [[1, 2, 3, 7], [4, 5, 6, 8]]

Yes

Intergritätsbedingungen

Um zu testen, ob ein Attributname nur für eine einzige Spalte einer Tabelle benutzt, wird schreiben wir folgende Regel:
database_error(duplicate_attribute(Table, Attribute, [N1, N2])) :-
   attribute(Table, N1, Attribute),
   attribute(Table, N2, Attribute),
   N1 \= N2.
Falls in der Definition der Tabellenstrukturen fäschlicherweise zwei Spalten mit demselben Attribut überschrieben wurden, etwa
attribute(t, 1, b).
attribute(t, 2, b).
attribute(t, 3, c).

...
so könnte das erkannt werden:
?- database_error(Message).

Message = duplicate_attribute(t, b, [1, 2])

Yes

Field Notation

Die folgende Repräsentation macht die explizite Repräsentation der Tabellenstrukturen mittels des Prädikats attribute überflüssig:
t([a:1, b:2, c:3]).
t([a:4, b:5, c:6]).

s([c:3, d:7]).
s([c:6, d:8]).
Diese Repräsentation entspricht ziehmlich genau der XML-Repräsentation:
<table name="t">
   <row a="1" b="2" c="3"/>
   <row a="4" b="5" c="6"/>
</table>

<table name="s">
   <row c="3" d="7"/>
   <row c="6" d="8"/>
</table>
Jetzt kann man wie folgt die Projektion auf ein Attribut berechnen:
select_all_values_from_table(Table, Attribute, Values) :-
   findall( Value,
      ( Goal =.. [Table, Tuple],
        call(Goal),
        member(Attribute:Value, Tuple) ),
      Values ).
Mittels metalinguistischer Abstraktion könnte man diese Regel noch vereinfachen:
Value := Table^Attribute :-
   Goal =.. [Table, Tuple],
   call(Goal),
   member(Attribute:Value, Tuple).

select_all_values_from_table(Table, Attribute, Values) :-
   findall( Value,
      Value := Table^Attribute,
      Values ).
Hier ist := ein Prädikatensymbol in Infix-Notation, welches auf die beiden Argumente Value und Table^Attribute angewendet wird. Das zweite Argument Table^Attribute ist dabei ein Term mit dem Infix-Funktionssymbol ^.