Wann werden geerbte Tabellen in PostgreSQL verwendet?

83

In welchen Situationen sollten Sie geerbte Tabellen verwenden? Ich habe versucht, sie sehr kurz zu verwenden, und die Vererbung schien nicht wie in der OOP-Welt.

Ich dachte, es funktioniert so:

Tabelle usersmit allen Feldern, die für alle Benutzerebenen erforderlich sind. Tabellen mögen moderators, admins, bloggers, usw. , aber Felder sind nicht von den Eltern überprüft. Zum Beispiel usershat E-Mail-Feld und geerbt bloggershat es jetzt auch, aber es ist nicht für beide usersund bloggersgleichzeitig eindeutig . dh. genauso wie ich beiden Tabellen ein E-Mail-Feld hinzufüge.

Nur Nutzung den ich denken konnte , sind Felder , die in der Regel verwendet werden, wie row_is_deleted , created_at , modified_at . Ist dies die einzige Verwendung für geerbte Tabellen?

Raspi
quelle

Antworten:

111

Es gibt einige Hauptgründe für die Verwendung der Tabellenvererbung in Postgres.

Nehmen wir an, wir haben einige Tabellen für Statistiken, die jeden Monat erstellt und gefüllt werden:

statistics
    - statistics_2010_04 (inherits statistics)
    - statistics_2010_05 (inherits statistics)

In diesem Beispiel haben wir 2.000.000 Zeilen in jeder Tabelle. Jede Tabelle hat eine CHECK-Einschränkung, um sicherzustellen, dass nur Daten für den passenden Monat darin gespeichert werden.

Was macht die Vererbung zu einer coolen Funktion - warum ist es cool, die Daten zu teilen?

  • LEISTUNG: Bei der Auswahl von Daten wählen wir * AUS Statistiken aus, wobei das Datum zwischen x und Y liegt, und Postgres verwendet nur die Tabellen, in denen dies sinnvoll ist. Z.B. SELECT * FROM statistics WHERE Datum ZWISCHEN '2010-04-01' UND '2010-04-15' scannt nur die Tabelle statistics_2010_04, alle anderen Tabellen werden nicht berührt - schnell!
  • Indexgröße: Wir haben keine große Fetttabelle mit einem großen Fettindex am Spaltendatum. Wir haben kleine Tabellen pro Monat mit kleinen Indizes - schnellere Lesevorgänge.
  • Wartung: Wir können Vakuum voll, neu indizieren und Cluster für jede Monatstabelle ausführen, ohne alle anderen Daten zu sperren

Informationen zur korrekten Verwendung der Tabellenvererbung als Leistungssteigerung finden Sie im postgresql-Handbuch. Sie müssen für jede Tabelle CHECK-Einschränkungen festlegen, um der Datenbank mitzuteilen, auf welchem ​​Schlüssel Ihre Daten aufgeteilt (partitioniert) werden.

Ich verwende die Tabellenvererbung stark, insbesondere wenn es darum geht, nach Monat gruppierte Protokolldaten zu speichern. Hinweis: Wenn Sie Daten speichern, die sich nie ändern (Protokolldaten), erstellen oder indizieren Sie mit CREATE INDEX ON () WITH (fillfactor = 100); Dies bedeutet, dass im Index kein Platz für Aktualisierungen reserviert wird - der Index ist auf der Festplatte kleiner.

UPDATE: Der Standardwert für Füllfaktoren ist 100 von http://www.postgresql.org/docs/9.1/static/sql-createtable.html :

Der Füllfaktor für eine Tabelle ist ein Prozentsatz zwischen 10 und 100. 100 (vollständige Verpackung) ist die Standardeinstellung

S38
quelle
13
Ein weiteres Beispiel für Partitionierung
Frank Heikens
4
Wie versteht Postgres in Ihrem Artikel 1, in welcher der Tabellen gesucht werden muss? Sie wählen aus der übergeordneten Tabelle aus, und der Datumsbereich ist nur ein praktisches Beispiel für die Aufteilung. Die übergeordnete Tabelle kann diese Logik nicht kennen. Oder irre ich mich?
Alexander Palamarchuk
4
Das Ausführen einer Abfrage für die übergeordnete Tabelle entspricht praktisch dem Ausführen einer Abfrage für eine UNION ALL für alle untergeordneten Tabellen in gemeinsamen Zeilen. Der Abfrageplaner kennt die Überprüfungsbeschränkungen, die jede Partition definieren, und verwendet sie, solange sie Partitionen nicht überlappen, um zu bestimmen, dass Überprüfungstabellen übersprungen werden können, für die CHECKs angeben, dass keine Zeilen zurückgegeben werden. Postgres docs auf diesem
zxq9
@avesus heh ... Der obige Code ist eines solchen Sarkasmus würdig. Es ist typisch, solche Dinge in eine Art Wartungsroutine zu packen. Dies kann so einfach sein wie eine gespeicherte Prozedur, die sich unter bestimmten Bedingungen, bei einem Cron-Job oder was auch immer darum kümmert. Es ist üblich, nach Datum zu partitionieren, aber ich habe festgestellt, dass ich von Zeit zu Zeit auch nach Tabellenbereichszuordnung partitioniere, und dies erfordert einige externe Informationen - die 30 Minuten, die zum Schreiben eines Partitionsbabysitters benötigt werden, sind es für die Steuerung wert es gibt dir.
zxq9
Hmm. Bist du sicher, dass es nicht blockiert? Ich habe ein ähnliches Setup, aber wenn ich den CLUSTER-Befehl auf einer einzelnen Partition ausführe, blockiert eine SELECT-Anweisung für Daten, die von einer anderen Partition gehalten werden!
E. van Putten
37

"Tabellenvererbung" bedeutet etwas anderes als "Klassenvererbung" und dient unterschiedlichen Zwecken.

Bei Postgres dreht sich alles um Datendefinitionen. Manchmal sehr komplexe Datendefinitionen. Bei OOP (im üblichen Java-farbigen Sinne) geht es darum, Verhaltensweisen Datendefinitionen in einer einzelnen Atomstruktur unterzuordnen. Der Zweck und die Bedeutung des Wortes "Vererbung" unterscheiden sich hier erheblich.

In OOP-Land könnte ich definieren (hier sehr locker mit Syntax und Semantik):

import life

class Animal(life.Autonomous):
  metabolism = biofunc(alive=True)

  def die(self):
    self.metabolism = False

class Mammal(Animal):
  hair_color = color(foo=bar)

  def gray(self, mate):
    self.hair_color = age_effect('hair', self.age)

class Human(Mammal):
  alcoholic = vice_boolean(baz=balls)

Die Tabellen hierfür könnten folgendermaßen aussehen:

CREATE TABLE animal
  (name       varchar(20) PRIMARY KEY,
   metabolism boolean NOT NULL);

CREATE TABLE mammal
  (hair_color  varchar(20) REFERENCES hair_color(code) NOT NULL,
   PRIMARY KEY (name))
  INHERITS (animal);

CREATE TABLE human
  (alcoholic  boolean NOT NULL,
   FOREIGN KEY (hair_color) REFERENCES hair_color(code),
   PRIMARY KEY (name))
  INHERITS (mammal);

Aber wo sind die Verhaltensweisen? Sie passen nirgendwo hin. Dies ist nicht der Zweck von "Objekten", wie sie in der Datenbankwelt diskutiert werden, da sich Datenbanken mit Daten befassen, nicht mit Verfahrenscode. Sie könnten Funktionen in die Datenbank schreiben, um Berechnungen für Sie durchzuführen (oft eine sehr gute Idee, aber nicht wirklich etwas, das in diesen Fall passt), aber Funktionen sind nicht dasselbe wie Methoden - Methoden, wie sie in Form von OOP verstanden werden, von dem Sie sprechen etwa sind bewusst weniger flexibel.

Bei der Vererbung als schematisches Gerät ist noch eines zu beachten: Ab Postgres 9.2 gibt es keine Möglichkeit, eine Fremdschlüsseleinschränkung auf alle Mitglieder der Partitionen / Tabellenfamilie gleichzeitig zu verweisen. Sie können Schecks schreiben, um dies zu tun, oder es auf andere Weise umgehen, aber es ist keine integrierte Funktion (es kommt wirklich auf Probleme mit der komplexen Indizierung an, und niemand hat die Bits geschrieben, die erforderlich sind, um dies automatisch zu machen). Anstatt die Tabellenvererbung für diesen Zweck zu verwenden, besteht eine bessere Übereinstimmung in der Datenbank für die Objektvererbung häufig darin, schematische Erweiterungen für Tabellen vorzunehmen. Etwas wie das:

CREATE TABLE animal
  (name       varchar(20) PRIMARY KEY,
   ilk        varchar(20) REFERENCES animal_ilk NOT NULL,
   metabolism boolean NOT NULL);

CREATE TABLE mammal
  (animal      varchar(20) REFERENCES animal PRIMARY KEY,
   ilk         varchar(20) REFERENCES mammal_ilk NOT NULL,
   hair_color  varchar(20) REFERENCES hair_color(code) NOT NULL);


CREATE TABLE human
  (mammal     varchar(20) REFERENCES mammal PRIMARY KEY,
   alcoholic  boolean NOT NULL);

Jetzt haben wir eine kanonische Referenz für die Instanz des Tieres, die wir zuverlässig als Fremdschlüsselreferenz verwenden können, und wir haben eine "ilk" -Spalte, die auf eine Tabelle mit xxx_ilk-Definitionen verweist, die auf die "nächste" Tabelle mit erweiterten Daten verweist ( oder zeigt an, dass es keine gibt, wenn der ilk der generische Typ selbst ist). Das Schreiben von Tabellenfunktionen, Ansichten usw. für diese Art von Schema ist so einfach, dass die meisten ORM-Frameworks genau dies im Hintergrund tun, wenn Sie auf die Klassenvererbung im OOP-Stil zurückgreifen, um Familien von Objekttypen zu erstellen.

zxq9
quelle
Was wäre, wenn Sie jedes bekannte Säugetier hinzufügen würden? Würden Sie von Säugetieren erben oder einen Fremdschlüssel haben, wie Sie es hier getan haben? Das Problem, das ich mit Fremdschlüsseln habe, ist, dass Sie am Ende so viele Joins durchführen müssen.
puk
1
@puk Sie müssten zuerst entscheiden, warum Sie jedes bekannte Säugetier hinzufügen. Die Form der Daten wird durch die Art und Weise bestimmt, wie die Daten verwendet werden (in diesem Fall ist es wahrscheinlich nicht erforderlich, eine Tabelle pro Tier zu haben - ziehen Sie Datenbanken für Spielbestiarien in Betracht, in denen Sie wirklich jede Art von Mob haben ). Im obigen Fall würde ich normalerweise eine Ansicht hinzufügen, die der häufigste Fall ist mammal JOIN human, nur weil es ärgerlich ist, jedes Mal einen Join zu schreiben. Aber nicht vermeiden verbindet . Joins sind das, was das R in RDBMS bringt. Wenn Sie Joins nicht mögen, sollten Sie einen anderen DB-Typ verwenden.
zxq9
@ zxq9: Ich vermute, dass bei massiven, ineffizienten Verknüpfungen aufgrund großer Tabellen materialisierte Ansichten ins Spiel kommen? (Ich habe Postgres nicht so lange benutzt)
Mark K Cowan
1
@ MarkKCowan Joins sind nicht ineffizient. Was ineffizient ist, ist der Versuch, nicht indizierten, nicht eindeutigen Feldern beizutreten (da das Schema nicht annähernd normalisiert werden kann), da das Design schlampig ist. In diesen Fällen kann eine materialisierte Ansicht hilfreich sein. Materialisierte Ansichten sind auch hilfreich, wenn Sie normalisierte Daten als schematische Grundlage benötigen (häufig zutreffend), aber auch mehrere funktionierende, denormalisierte Darstellungen benötigen, mit denen Sie entweder für die Verarbeitungseffizienz (Berechnung vorab laden) oder für die kognitive Effizienz einfacher arbeiten können. Wenn Sie mehr schreiben als lesen, ist dies jedoch eine Pessimierung.
zxq9
1
@MarkKCowan "Langsam" ist ein relativer Begriff. In großen Geschäftssystemen und Spieleservern, auf denen wir ~ 50 ms für die Rückgabe einer Abfrage akzeptieren können, waren 20 Tabellenverknüpfungen meiner Erfahrung nach (in Postgres 8+ jedenfalls) nie ein Problem. Aber in Fällen, in denen das Management <1 ms Antworten auf> 10b Zeilenverknüpfungen in mehr als 5 Tabellen mit nicht indizierten Daten (oder abgeleiteten Werten!) Wünscht, fühlt sich kein System auf der Welt "schnell" an, außer diesen Beitritt im letzten Monat durchzuführen und ihn zu speichern in einem schnellen K / V-Geschäft (was im Wesentlichen das ist, was eine materialisierte Ansicht unter besonderen Umständen bewirken kann). Ich kann mich weder beim Schreiben noch beim Lesen einem Kompromiss entziehen.
zxq9
6

Vererbung kann in einem OOP-Paradigma verwendet werden, solange Sie keine Fremdschlüssel in der übergeordneten Tabelle erstellen müssen. Wenn Sie beispielsweise ein Fahrzeug der abstrakten Klasse in einer Fahrzeugtabelle gespeichert haben und ein Tischauto, das davon erbt, werden alle Fahrzeuge in der Fahrzeugtabelle angezeigt, aber ein Fremdschlüssel aus einer Fahrertabelle auf der Fahrzeugtabelle stimmt nicht mit diesen überein Aufzeichnungen.

Die Vererbung kann auch als Partitionierungswerkzeug verwendet werden. Dies ist besonders nützlich, wenn Sie Tabellen haben, die für immer wachsen sollen (Protokolltabellen usw.).

grégoire hubert
quelle
1
Tabelleneinschränkungen werden nicht vererbt, es handelt sich also um mehr als nur Fremdschlüssel. Sie können die Tabelleneinschränkungen auf die untergeordneten Tabellen anwenden, die in Ihrer DDL erstellt wurden, oder Sie können Trigger schreiben, um dieselben Einschränkungen zu bewirken.
Wexxor
3

Die Hauptverwendung der Vererbung ist die Partitionierung, manchmal jedoch auch in anderen Situationen. In meiner Datenbank gibt es viele Tabellen, die sich nur in einem Fremdschlüssel unterscheiden. Meine "abstrakte Klasse" -Tabelle "Bild" enthält eine "ID" (Primärschlüssel dafür muss in jeder Tabelle enthalten sein) und ein PostGIS 2.0-Raster. Vererbte Tabellen wie "site_map" oder "artefakt_drawing" haben eine Fremdschlüsselspalte (Textspalte "site_name" für "site_map", "Artefakt_id" Ganzzahlspalte für die Tabelle "artefakt_drawing" usw.) sowie Primär- und Fremdschlüsseleinschränkungen. Der Rest wird von der "Bild" -Tabelle geerbt. Ich vermute, dass ich in Zukunft möglicherweise allen Bildtabellen eine "Beschreibungs" -Spalte hinzufügen muss, sodass ich mir eine Menge Arbeit ersparen kann, ohne echte Probleme zu machen (na ja,

BEARBEITEN: eine weitere gute Verwendung: Bei der Behandlung von nicht registrierten Benutzern mit zwei Tabellen haben andere RDBMS Probleme mit der Behandlung der beiden Tabellen, aber in PostgreSQL ist dies einfach - fügen ONLYSie einfach hinzu, wenn Sie nicht an Daten in der geerbten Tabelle "nicht registrierter Benutzer" interessiert sind .

Pavel V.
quelle
2

Die einzige Erfahrung, die ich mit geerbten Tabellen habe, ist das Partitionieren. Es funktioniert gut, aber es ist nicht der raffinierteste und benutzerfreundlichste Teil von PostgreSQL.

Letzte Woche hatten wir das gleiche OOP-Problem, aber wir hatten zu viele Probleme mit dem Ruhezustand (unser Setup gefiel uns nicht), sodass wir die Vererbung in PostgreSQL nicht verwendeten.

Frank Heikens
quelle
0

Ich verwende die Vererbung, wenn ich mehr als 1: 1-Beziehungen zwischen Tabellen habe.

Beispiel: Angenommen, Sie möchten Objektkartenpositionen mit den Attributen x, y, Drehung und Skalierung speichern.

Angenommen, Sie müssen verschiedene Arten von Objekten auf der Karte anzeigen, und jedes Objekt verfügt über eigene Kartenstandortparameter. Kartenparameter werden niemals wiederverwendet.

In diesen Fällen wäre die Tabellenvererbung sehr nützlich, um zu vermeiden, dass nicht normalisierte Tabellen verwaltet oder Standort-IDs erstellt und auf andere Tabellen verwiesen werden müssen.

Maarten
quelle
-4

Verwenden Sie es so wenig wie möglich. Und das bedeutet normalerweise nie, es läuft darauf hinaus, Strukturen zu schaffen, die gegen das relationale Modell verstoßen, indem beispielsweise das Informationsprinzip gebrochen und Taschen anstelle von Beziehungen geschaffen werden.

Verwenden Sie stattdessen die Tabellenpartitionierung in Kombination mit einer ordnungsgemäßen relationalen Modellierung, einschließlich weiterer Normalformen.

Leandro
quelle
4
Es ist nicht wahr, dass die Vererbungsfunktion von PostgreSQL das relationale Modell verletzt, indem es das Informationsprinzip verletzt. Das Informationsprinzip besagt, dass alle Daten in einer relationalen Datenbank durch Datenwerte in Relationen dargestellt werden und alle Abfrageergebnisse wieder als Relation dargestellt werden. ( En.wikipedia.org/wiki/Relational_model ) Dies ist immer der Fall, da alle Tabellen , die eine andere Tabelle erben, sind wieder einfache Tabellen. Aus diesem Grund gibt es auch keine "Tasche", was auch immer das bedeutet.
Roland
2
Nun, Wikipedia ist kaum eine Referenz in Bezug auf das relationale Modell; Es weigert sich zu erkennen, dass SQL gegen das relationale Modell verstößt. Eine Tasche ist eine Tabelle ohne Schlüssel, da sie möglicherweise Duplikate enthält und somit keine Beziehung darstellt. Eine Beziehung muss eine Menge sein.
Leandro
Das ist kein Problem der Funktion selbst, sondern wie sie verwendet wird. Wenn Sie mit UUIDs als Bezeichner arbeiten, verfügen Sie über eindeutige Schlüssel für alle Untertabellen.
Roland
Sie haben einen Punkt, aber das Problem hier ist, dass die Vererbung den Modellierer dazu bringt, das relationale Modell zu ignorieren. UUIDs sind keine echten Schlüssel, sondern Ersatzschlüssel. Man muss noch natürliche Schlüssel deklarieren.
Leandro