Postgresql

使用表繼承而不是映射表

  • August 28, 2017

這似乎是一個非常常見的場景:幾種類型都構成相同的子類型。

這通常看起來像這樣:

-- 'name' is unique per parent record
CREATE TABLE sometype (
 sometype_id serial PRIMARY KEY,
 name        text
);

CREATE TABLE foo (foo_id serial PK);
CREATE TABLE bar (bar_id serial PK);

CREATE TABLE foo_sometype (
 foo_id      int4,
 sometype_id int4
);

CREATE TABLE bar_sometype (
 bar_id      int4,
 sometype_id int4
);

這很好,但查詢起來很麻煩。我認為這可能更清潔:

-- 'name' is unique per parent record
CREATE TABLE sometype (
 name        text
);

CREATE TABLE foo_sometype (
 foo_id      int4
) INHERITS(sometype);

CREATE TABLE bar_sometype (
 bar_id      int4,
) INHERITS(sometype);

我喜歡這個:

  • 加入簡單(with USING
  • 無需為“sometype”添加代理鍵,它明確是“foo”和“bar”的組件

不過,這似乎是一種非典型的繼承用法。

有什麼理由這樣做?

論繼承的“不足”

請注意,Pg 繼承的標準警告僅在表繼承用於直接建模類繼承時才相關,這不是我在這裡所做的。事實上,為了讓它工作,我需要繼承來表現它的行為方式。

我幾乎希望他們將其稱為“繼承”以外的其他東西,因為這種行為非常合乎邏輯,並且“缺點”僅與一個案例相關。

優於手動複製表結構的好處

正如 Evan 所指出的,我可以手動創建看起來與我描述的完全一樣的“foo_sometype”和“bar_sometype”,但我認為繼承結構有幾個顯著的好處:

  • 繼承關係明確地將 ‘foo_sometype’ 和 ‘bar_sometype’ 定義為相同類型,而不僅僅是恰好具有相同列的兩個表。
  • 通過父表進行未來的模式更改可以減少意外分歧的機會(通過一些工作,這實際上可以強制執行)。
  • 更重要的是,可以針對父表生成客戶端程式碼,並將其應用於僅更改表名的子表,並且(再次)確信執行了表結構。

所以,作為一個人為的例子,Foo 和 Bar 可以有一個 HasSomeTypeList 特徵,它抽象了所有“sometype”操作,並且知道兩個表都可以映射到 SomeType 類。

表示 Foo/Bar 關係,無論是建模為 trait 還是繼承,都是這裡的最終目標。

順便說一句,對於天真的使用者/查詢編寫者——他們不希望進行模式更改——這兩種方式看起來是一樣的。

繼承是我不會觸及的那些特性之一。AFAIK,它在內部用於某些容量的複制和分區。我不確定它是否是為了最終使用者使用而設計的。

具體的技術缺點

UNIQUE 和 REFERENCES 的缺點

該文件涵蓋了CAVEAT 部分中的一些缺點(以下很重要)。

  • 如果我們將 parent.name 聲明為 UNIQUE 或 PRIMARY KEY,這不會阻止子表的行名稱與父表中的行重複。預設情況下,這些重複的行會顯示在來自父級的查詢中。事實上,預設情況下 child 根本沒有唯一約束,因此可以包含多個具有相同名稱的行。您可以為子級添加唯一約束,但這不會阻止與父級相比的重複。
  • 類似地,如果我們指定 parent.name REFERENCES 某個其他表,則此約束不會自動傳播到子表。在這種情況下,您可以通過手動將相同的 REFERENCES 約束添加到子級來解決它。
  • 指定另一個表的列 REFERENCES parent(name) 將允許另一個表包含父名稱,但不允許包含子名稱。這種情況沒有好的解決方法。

開發 INHERITs 進展緩慢

這些缺陷在 1996 年發布的7.3 文件中首次提到,儘管它們在實現繼承後就存在

這個缺陷可能會在未來的某個版本中得到修復。

唯一的變化是使2010 年發布的8.0 文件中的缺陷更加明確和詳細。

這些缺陷可能會在未來的某個版本中得到修復,但與此同時,在確定繼承是否對您的問題有用時需要相當小心。

祝你好運,等待未來的發布。而且,您談論的某些功能並不是構圖所獨有的,

保存“鑰匙”沒有實際意義

  • ‘sometype’ 上沒有代理鍵,它明確地是一個組合

這與製作sometype屬性列表並直接連結到它有什麼不同?

CREATE TABLE sometype (sometype_name text PRIMARY KEY);
CREATE TABLE foo (foo_id serial PRIMARY KEY);
CREATE TABLE foo_sometype (
 foo_id int REFERENCES foo,
 sometype_name text REFERENCES sometype,
 PRIMARY KEY ( foo_id, sometype_name )
);

現在您甚至不必加入foo_sometype即可sometype獲得sometype.sometype_name.

表分區

除了所有這些問題,即將發布的 PostgreSQL 10 表分區版本變得更糟

不允許多重繼承,分區和繼承不能混用

所以你想要繼承?放棄分區,這實際上具有真正的規劃者優勢。

更改表

唉,ALTER TABLE它的註釋中也列出了很多缺點,

如果一個表有任何後代表,則不允許添加、重命名或更改列的類型,或重命名父表中的繼承約束,而不對後代做同樣的事情。也就是說,ALTER TABLE ONLY 將被拒絕。這可確保後代始終具有與父級匹配的列。

$$ … $$遞歸 DROP COLUMN 操作將刪除後代表的列,僅當後代不從任何其他父級繼承該列並且從未有該列的獨立定義時。非遞歸 DROP COLUMN(即 ALTER TABLE ONLY … DROP COLUMN)永遠不會刪除任何後代列,而是將它們標記為獨立定義而不是繼承。$$ … $$TRIGGER、CLUSTER、OWNER 和 TABLESPACE 操作永遠不會遞歸到後代表;也就是說,它們總是表現得好像只指定了一樣。僅對未標記為 NO INHERIT 的 CHECK 約束重複添加約束。

結論

我不認為很多人使用繼承。我從來沒有在野外見過它。數據庫中的繼承增加了學習曲線,並且一些特性最好不要管。您不必為他們找到應用程序。

您可能會發現Stack Overflow 上的這篇文章很有用,“何時在 PostgreSQL 中使用繼承的表?”。

提到的缺陷不是不使用繼承的理由!繼承在這裡的工作類似於具有獨立對象的類繼承。你可能有一個類/表“水果”和一個類/表“蘋果”和“橙子”。由於蘋果和橙子從水果中繼承了它們的元定義,因此您可以通過水果獲取它們。然而,它們是獨立的類,具有獨立的列舉,還有什麼你可以期待的……

如果您確實需要防止衝突:在主表上定義觸發器或檢查/外鍵(不包含)。

但是,請不要僅僅因為您對 PostgreSQL 中繼承的工作方式不滿意,就不要阻止人們使用它!獨立的子類或表格很棒!如果你需要一些依賴 - 以你需要的方式實現它。有很多很好的教程,包括官方文件。這是另一個例子:繼承——另一個喜歡 PostgreSQL 的理由

我也在我的一個項目中使用繼承,儘管可能存在主鍵衝突(我的父表沒有定義主鍵,我的模型不需要它)。

還有一些方法可以使用繼承來提高性能,不僅僅是 ORM。

難不等於壞。

引用自:https://dba.stackexchange.com/questions/157398