何时在PostgreSQL中使用继承表?

ras*_*spi 74 postgresql

在哪些情况下你应该使用继承表?我试图非常简单地使用它们,并且继承在OOP世界中似乎不像.

我认为它的工作原理如下:

users包含所有用户级别所需的所有字段的表.表像moderators,admins,bloggers,等,但字段从父检查.例如,users有电子邮件字段和继承bloggers现在也有它,但它并不是唯一的两个usersbloggers同时.即.就像我向两个表添加电子邮件字段一样.

只有我能想到的用法是通常使用的字段,如row_is_deleted,created_at,modified_at.这是继承表的唯一用法吗?

小智 105

在postgres中使用表继承有一些主要原因.

可以说,我们有一些统计所需的表,每个月都会创建和填充:

statistics
    - statistics_2010_04 (inherits statistics)
    - statistics_2010_05 (inherits statistics)
Run Code Online (Sandbox Code Playgroud)

在此示例中,每个表中有2.000.000行.每个表都有一个CHECK约束,以确保只有匹配月份的数据存储在其中.

那么是什么让继承成为一个很酷的功能 - 为什么分割数据很酷?

  • 性能:当选择数据时,我们SELECT*FROM statistics WHERE date BETWEEN x和Y,而Postgres只使用表,这是有意义的.例如.SELECT*FROM statistics WHERE date BETWEEN'2010-04-01'and'2010-04-15'仅扫描表statistics_2010_04,所有其他表格都不会被触及 - 快!
  • 索引大小:我们没有大的胖表,列日期有大的脂肪指数.我们每个月都有小表,索引很小 - 读取速度更快.
  • 维护:我们可以在每个月的表上运行vacuum full,reindex,cluster,而无需锁定所有其他数据

要正确使用表继承作为性能助推器,请查看postgresql手册.您需要在每个表上设置CHECK约束以告知数据库,您的数据被分割(分区)在哪个键上.

我大量使用表继承,特别是在按月分组存储日志数据时.提示:如果存储永远不会更改的数据(日志数据),则使用CREATE INDEX ON()WITH(fillfactor = 100)创建或索引; 这意味着索引中不会保留更新空间 - 索引在磁盘上较小.

更新:fillfactor默认值为100,来自http://www.postgresql.org/docs/9.1/static/sql-createtable.html:

表的fillfactor是介于10和100之间的百分比.100(完全打包)是默认值

  • 分区的另一个例子 (13认同)
  • 在您的第1项中,Postgres如何理解需要搜索哪些表?您可以从父表中进行选择,而日期范围只是拆分的一个方便示例.父表无法知道这个逻辑.或者我错了? (4认同)
  • 对父表执行查询实际上与在公共行上的每个后代表上对UNION ALL执行查询相同.查询计划程序知道定义每个分区的检查约束,并且只要它们不重叠,分区就会使用它们来确定它可以跳过检查CHECK表明不会返回任何行的表.[Postgres docs on this](http://www.postgresql.org/docs/9.1/static/ddl-partitioning.html) (4认同)

zxq*_*xq9 33

"表继承" 意味着与"类继承"不同的东西,它们用于不同的目的.

Postgres是关于数据定义的.有时真正复杂的数据定义.OOP(在常见的Java颜色的意义上)是关于在单个原子结构中对数据定义的从属行为."继承"一词的目的和意义在这里有很大的不同.

在OOP领域,我可能会定义(这里语法和语义非常松散):

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)
Run Code Online (Sandbox Code Playgroud)

这个表可能如下所示:

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);
Run Code Online (Sandbox Code Playgroud)

但这些行为在哪里?他们不适合任何地方.这不是数据库世界中讨论的"对象"的目的,因为数据库涉及数据,而不涉及过程代码.您可以在数据库中编写函数来为您进行计算(通常是一个非常好的主意,但不是真正符合这种情况的东西)但是函数与方法不同 - 正如您所说的OOP形式所理解的方法关于故意不那么灵活.

还有一点需要指出继承作为原理图设备:从Postgres 9.2开始,无法同时在所有分区/表族成员中引用外键约束.您可以编写检查来执行此操作或以其他方式绕过它,但它不是内置功能(它归结为复杂索引的问题,实际上,没有人编写了使自动化所需的位).为此目的而不是使用表继承,通常在数据库中更好地匹配对象继承是对表进行原型扩展.像这样的东西:

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);
Run Code Online (Sandbox Code Playgroud)

现在我们有一个关于动物实例的规范参考,我们可以可靠地将它作为外键引用,我们有一个"ilk"列,引用一个xxx_ilk定义表,指向扩展数据的"下一个"表(或者表示如果类型是通用类型本身则没有.针对这种模式编写表函数,视图等是如此简单,以至于当您使用OOP样式的类继承来创建对象类型族时,大多数ORM框架在后台完成此类操作.

  • @puk 您首先需要决定*为什么*要添加所有已知的哺乳动物。数据的形状将取决于数据的使用方式(在这种情况下,可能没有必要为每种动物设置一张表格——考虑游戏动物寓言的数据库,其中您确实拥有各种类型的暴徒)。在上面的例子中,我通常会添加一个视图,这是最常见的 `mammal JOIN human` 情况,因为每次都写一个连接很烦人。但是*不要避免连接*。联接是将 R 放入 RDBMS 的原因。如果您不喜欢连接,您应该使用不同的数据库类型。 (2认同)
  • @MarkKCowan 加入并不是低效的。由于草率的设计,试图加入非索引、非唯一字段(因为模式远未接近规范化)是低效的。在这些情况下,物化视图可能会有所帮助。如果您需要规范化数据作为原理图基础(通常是正确的),物化视图也很有帮助,但也需要一些工作的非规范化表示,这些表示更易于使用以提高处理效率(预先加载计算)或认知效率。不过,如果你写的比读的多,那就是悲观了。 (2认同)
  • @MarkKCowan“慢”是一个相对术语。在我们可以接受约 50 毫秒返回查询的大型业务系统和游戏服务器中,根据我的经验,20 个表连接从来都不是问题(无论如何在 Postgres 8+ 中)。但是,如果管理层希望对未索引数据(或派生值!)的 5 个以上表中的 >10b 行连接做出 <1 毫秒响应......在快速 K/V 存储中(这本质上是物化视图在特殊情况下的作用)。在写入或读取时都无法逃避权衡。 (2认同)

小智 5

只要您不需要在父表上创建外键,就可以在OOP范例中使用继承.例如,如果您有一个存储在车辆表中的抽象类车辆和从其继承的表车,则车辆表中将显示所有车辆,但车辆表上的驾驶员表中的外键将不匹配记录.

继承也可以用作分区工具.当你有永远成长的表(日志表等)时,这尤其有用.