¿Cuándo usar tablas heredadas en PostgreSQL?

84

¿En qué situaciones debería utilizar tablas heredadas? Traté de usarlos muy brevemente y la herencia no parecía en el mundo OOP.

Pensé que funcionaba así:

Tabla usersque tiene todos los campos requeridos para todos los niveles de usuario. Mesas como moderators, admins, bloggers,, etc, pero los campos están no comprobados de los padres. Por ejemplo, userstiene un campo de correo electrónico y heredado también lo bloggerstiene ahora, pero no es único para ambos usersy bloggersal mismo tiempo. es decir. Igual que agrego el campo de correo electrónico a ambas tablas.

El único uso que se me ocurre son los campos que se utilizan habitualmente, como row_is_deleted , created_at , modified_at . ¿Es este el único uso de tablas heredadas?

raspi
fuente

Respuestas:

111

Hay algunas razones importantes para usar la herencia de tablas en postgres.

Digamos que tenemos algunas tablas necesarias para las estadísticas, que se crean y completan cada mes:

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

En esta muestra, tenemos 2.000.000 filas en cada tabla. Cada tabla tiene una restricción CHECK para asegurarse de que solo los datos del mes coincidente se almacenen en ella.

Entonces, ¿qué hace que la herencia sea una característica interesante? ¿Por qué es genial dividir los datos?

  • RENDIMIENTO: Al seleccionar los datos, SELECCIONAMOS * DE las estadísticas DONDE fecha ENTRE x e Y, y Postgres solo usa las tablas, donde tiene sentido. P.ej. SELECCIONE * DE las estadísticas DONDE la fecha ENTRE '2010-04-01' Y '2010-04-15' solo escanea la tabla statistics_2010_04, todas las demás tablas no se tocarán, ¡rápido!
  • Tamaño del índice: no tenemos una tabla grande con un índice grande en la fecha de la columna. Tenemos tablas pequeñas por mes, con índices pequeños, lecturas más rápidas.
  • Mantenimiento: podemos ejecutar vacío completo, reindexar, agrupar en cada tabla de mes sin bloquear todos los demás datos

Para el uso correcto de la herencia de tablas como refuerzo del rendimiento, consulte el manual de postgresql. Debe establecer restricciones CHECK en cada tabla para indicarle a la base de datos en qué clave se dividen (particionan) sus datos.

Hago un uso intensivo de la herencia de tablas, especialmente cuando se trata de almacenar datos de registro agrupados por mes. Sugerencia: si almacena datos, que nunca cambiarán (datos de registro), cree o indexe con CREATE INDEX ON () WITH (fillfactor = 100); Esto significa que no se reservará espacio para actualizaciones en el índice; el índice es más pequeño en el disco.

ACTUALIZACIÓN: el factor de relleno predeterminado es 100, de http://www.postgresql.org/docs/9.1/static/sql-createtable.html :

El factor de relleno de una tabla es un porcentaje entre 10 y 100. 100 (embalaje completo) es el valor predeterminado

S38
fuente
13
Otro ejemplo de partición
Frank Heikens
4
En su elemento 1, ¿cómo entiende Postgres en cuál de las tablas se necesita buscar? Selecciona de la tabla principal, y el rango de fechas es solo un ejemplo conveniente de división. La tabla principal no puede conocer esta lógica. ¿O estoy equivocado?
Alexander Palamarchuk
4
Realizar una consulta en la tabla principal es efectivamente lo mismo que realizar una consulta en UNION ALL en todas las tablas descendientes en filas comunes. El planificador de consultas es consciente de las restricciones de comprobación que definen cada partición y, siempre que no se superpongan, las particiones las utiliza para determinar que puede omitir las tablas de comprobación para las que las CHECK indican que no se devolverán filas.
Documentos de
@avesus heh ... El código anterior tomado por sí solo es digno de tal sarcasmo. Es típico envolver este tipo de cosas en una rutina de mantenimiento de algún tipo. Esto puede ser tan simple como un procedimiento almacenado que se encarga de ello en alguna condición, un trabajo cron o lo que sea. Es común dividir por fecha, pero también me he encontrado particionando por asignación de espacio de tabla de vez en cuando, y eso requiere información externa: los 30 minutos que se necesitan para escribir una partición de niñera bien valen la pena para el control Te lo dá.
zxq9
Hmm. ¿Estás seguro de que no bloquea? Tengo una configuración similar, pero cuando ejecuto el comando CLUSTER en una sola partición, ¡una instrucción SELECT en los datos retenidos por otros bloques de partición!
E. van Putten
37

"Herencia de tabla" significa algo diferente a "herencia de clase" y sirven para diferentes propósitos.

Postgres se trata de definiciones de datos. A veces, definiciones de datos realmente complejas. OOP (en el sentido común de las cosas del color de Java) se trata de subordinar comportamientos a definiciones de datos en una sola estructura atómica. El propósito y el significado de la palabra "herencia" es significativamente diferente aquí.

En OOP land podría definir (siendo muy flexible con la sintaxis y la semántica aquí):

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)

Las tablas para esto podrían verse así:

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);

Pero, ¿dónde están los comportamientos? No caben en ninguna parte. Este no es el propósito de los "objetos" como se discuten en el mundo de las bases de datos, porque las bases de datos están relacionadas con datos, no con códigos de procedimiento. Puede escribir funciones en la base de datos para hacer cálculos por usted (a menudo es una muy buena idea, pero no es realmente algo que se ajuste a este caso), pero las funciones no son lo mismo que los métodos: métodos tal como se entienden en la forma de POO que está hablando sobre son deliberadamente menos flexibles.

Hay una cosa más que señalar sobre la herencia como dispositivo esquemático: a partir de Postgres 9.2, no hay forma de hacer referencia a una restricción de clave externa en todas las particiones / miembros de la familia de tablas a la vez. Puede escribir cheques para hacer esto o evitarlo de otra manera, pero no es una característica incorporada (se reduce a problemas con la indexación compleja, en realidad, y nadie ha escrito los bits necesarios para que eso sea automático). En lugar de usar la herencia de tablas para este propósito, a menudo una mejor coincidencia en la base de datos para la herencia de objetos es hacer extensiones esquemáticas a las tablas. Algo como esto:

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);

Ahora tenemos una referencia canónica para la instancia del animal que podemos usar de manera confiable como referencia de clave externa, y tenemos una columna "calaña" que hace referencia a una tabla de definiciones xxx_ilk que apunta a la tabla "siguiente" de datos extendidos ( o indica que no hay ninguno si el tipo es el tipo genérico en sí). Escribir funciones de tabla, vistas, etc. contra este tipo de esquema es tan fácil que la mayoría de los marcos de ORM hacen exactamente este tipo de cosas en segundo plano cuando se recurre a la herencia de clases de estilo OOP para crear familias de tipos de objetos.

zxq9
fuente
¿Y si agregara todos los mamíferos conocidos? ¿Heredaría de un mamífero o tendría una clave externa como la que tenía aquí? El problema que tengo con las claves externas es que terminas teniendo que hacer muchas uniones.
puk
1
@puk Primero tendría que decidir por qué estaba agregando todos los mamíferos conocidos. La forma de los datos estará determinada por la forma en que se utilizarán los datos (probablemente no sea necesario tener una tabla por animal en este caso; considere las bases de datos para bestiarios de juegos donde realmente tiene todo tipo de mafia). ). En el caso anterior, normalmente agregaría una vista que es el caso más común de mammal JOIN human, solo porque escribir una combinación cada vez es molesto. Pero no evite las uniones . Las uniones son lo que pone la R en RDBMS. Si no le gustan las uniones, debe usar un tipo de base de datos diferente.
zxq9
@ zxq9: Supongo que las uniones masivas e ineficientes debidas a tablas grandes es donde entran en juego las vistas materializadas. (No he estado usando Postgres durante tanto tiempo)
Mark K Cowan
1
Las uniones de @MarkKCowan no son ineficientes. Lo que es ineficiente es intentar unirse en campos no indexados ni únicos (porque el esquema no está ni cerca de estar normalizado) debido a un diseño descuidado. En esos casos, una vista materializada puede resultar útil. Las vistas materializadas también son útiles en el caso de que necesite datos normalizados como base esquemática (a menudo es cierto), pero también necesita varias representaciones desnormalizadas y funcionales con las que sea más fácil trabajar para la eficiencia del procesamiento (carga frontal del cálculo) o la eficiencia cognitiva. Sin embargo, si escribe más de lo que lee, es una pesimización.
zxq9
1
@MarkKCowan "Lento" es un término relativo. En los grandes sistemas empresariales y servidores de juegos donde podemos aceptar ~ 50ms para devolver una consulta, 20 combinaciones de tablas nunca han sido un problema (en Postgres 8+, de todos modos) en mi experiencia. Pero en los casos en los que la gerencia quiere respuestas de <1 ms a> 10 mil millones de combinaciones de filas en más de 5 tablas con datos no indexados (¡o valores derivados!) ... ningún sistema en el mundo se sentirá "rápido" aparte de hacer esta combinación el mes pasado y guardarla. en una tienda rápida de K / V (que es esencialmente lo que puede actuar una vista materializada en circunstancias especiales). No se puede escapar de una compensación en el tiempo de lectura o escritura.
zxq9
6

La herencia se puede usar en un paradigma de programación orientada a objetos siempre que no necesite crear claves externas en la tabla principal. Por ejemplo, si tiene un vehículo de clase abstracta almacenado en una tabla de vehículos y una tabla de automóvil que hereda de él, todos los automóviles serán visibles en la tabla de vehículos, pero una clave externa de una tabla de conductores en la tabla de vehículos no coincidirá con las tesis. registros.

La herencia también se puede utilizar como herramienta de partición . Esto es especialmente útil cuando tiene tablas destinadas a crecer para siempre (tablas de registro, etc.).

Grégoire Hubert
fuente
1
Las restricciones de tabla no se heredan, por lo que es más que solo claves externas. Puede aplicar las restricciones de la tabla en las tablas secundarias a medida que se crean en su DDL, o puede escribir activadores para aplicar las mismas restricciones.
Wexxor
3

El uso principal de la herencia es para particionar, pero a veces es útil en otras situaciones. En mi base de datos hay muchas tablas que difieren solo en una clave externa. Mi "imagen" de tabla de "clase abstracta" contiene una "ID" (la clave principal debe estar en cada tabla) y un ráster PostGIS 2.0. Las tablas heredadas como "site_map" o "artifact_drawing" tienen una columna de clave externa (columna de texto "site_name" para "site_map", columna de entero "artifact_id" para la tabla "artifact_drawing", etc.) y restricciones de clave primaria y externa; el resto se hereda de la tabla "imagen". Sospecho que podría tener que agregar una columna de "descripción" a todas las tablas de imágenes en el futuro, por lo que esto podría ahorrarme bastante trabajo sin generar problemas reales (bueno,

EDITAR: otro buen uso: con el manejo de dos tablas de usuarios no registrados , otros RDBMS tienen problemas con el manejo de las dos tablas, pero en PostgreSQL es fácil: solo agregue ONLYcuando no esté interesado en los datos en la tabla heredada de "usuarios no registrados".

Pavel V.
fuente
2

La única experiencia que tengo con las tablas heredadas es la partición. Funciona bien, pero no es la parte más sofisticada y fácil de usar de PostgreSQL.

La semana pasada buscábamos el mismo problema de programación orientada a objetos, pero teníamos demasiados problemas con Hibernate (no nos gustó nuestra configuración), por lo que no usamos la herencia en PostgreSQL.

Frank Heikens
fuente
0

Utilizo la herencia cuando tengo más de 1 en 1 relaciones entre tablas.

Ejemplo: suponga que desea almacenar ubicaciones de mapas de objetos con atributos x, y, rotación, escala.

Ahora suponga que tiene varios tipos diferentes de objetos para mostrar en el mapa y cada objeto tiene sus propios parámetros de ubicación del mapa, y los parámetros del mapa nunca se reutilizan.

En estos casos, la herencia de tablas sería bastante útil para evitar tener que mantener tablas no normalizadas o tener que crear identificadores de ubicación y hacer referencias cruzadas con otras tablas.

Maarten
fuente
-4

Úselo lo menos posible. Y eso generalmente significa nunca, se reduce a una forma de crear estructuras que violan el modelo relacional, por ejemplo, rompiendo el principio de información y creando bolsas en lugar de relaciones.

En su lugar, utilice la partición de tablas combinada con el modelado relacional adecuado, incluidas otras formas normales.

Leandro
fuente
4
No es cierto que la característica de herencia de PostgreSQL viole el modelo relacional al romper el principio de información. El principio de información dice que todos los datos en una base de datos relacional están representados por valores de datos en relaciones y todos los resultados de la consulta se representan nuevamente como una relación ( en.wikipedia.org/wiki/Relational_model ) Este es siempre el caso, ya que todas las tablas , que heredan otra tabla, son tablas simples nuevamente. Por esa razón tampoco existe una "bolsa", sea lo que sea que eso signifique.
Roland
2
Bueno, Wikipedia no es una referencia en cuanto al modelo relacional; se niega a reconocer que SQL viola el modelo relacional. Una bolsa es una mesa sin llave, porque potencialmente tiene duplicados, por lo que no es una relación; una relación debe ser un conjunto.
Leandro
Ese no es un problema de la función en sí, sino de cómo se usa. Si trabaja con uuids como identificadores, tendrá claves únicas en todas las subtablas.
Roland
Tiene razón, pero el problema aquí es que la herencia lleva al modelador a ignorar el modelo relacional. Los UUID no son claves reales, sino sustitutas. Todavía hay que declarar claves naturales.
Leandro