這張表有主鍵,就認得出每一筆資料嗎?主鍵、唯一鍵、代理鍵與 rowid 的兩個判斷維度
有主鍵,就認得出每一筆資料嗎?把「主鍵、唯一鍵」這種角色,和「自然鍵、代理鍵」這種值的來源拆開,用兩個維度判斷一把鍵能不能用:此刻認得準,明天還認得。附會員合併的逐步實測、外鍵的極限,與五種鍵的可用程度排序。
有一次跟顧問討論 CDC,他建議:把現有的唯一鍵升成主鍵就好。我心裡浮起一個問題:那跟另外加一個流水號(serial)當主鍵,有什麼不同?對 CDC 來說,差在哪裡?
類似的對話,我在不同場合聽過好幾個版本:
「這張表有主鍵,接 CDC 沒問題。」
「主鍵是哪一欄?」
「會員編號。」
「會員編號會變嗎?」
「不會,那是系統發的號。」
一開始的設計看起來都沒問題。直到業務上需要合併會員:兩個會員帳號被認定是同一個人,其中一個會員編號被併進另一個。那個「系統發的、不會變的號」,變了。
這段對話的背景是 CDC:〈數據中台 CDC 開了,資料就會同步嗎?〉那篇談過,CDC 把來源系統的每一筆變更讀出來之後,要靠一把「鍵」把它套到目標端正確的那一列。
對話裡的兩個人都沒說錯。第一個人說的是角色:會員編號在資料庫裡被宣告成主鍵,這件事查得到。第二個人問的是值會不會變:這件事在 schema(資料庫的結構定義)上查不到,要問業務。兩句話講的是兩個不同的維度,卻被塞進同一個詞「主鍵」裡談,所以怎麼談都談不攏。
這篇把這兩個維度拆開。讀完之後,你應該能對任何一把鍵回答兩個問題:它此刻認不認得準?明天還認不認得?
一、兩件事:此刻認得準,明天還認得
維度一:每一筆都有值,而且不重複
一把鍵要能認出一筆資料,最基本的條件是:拿這個值去找,只會找到一列。這需要兩件事同時成立——值不重複,而且每一筆都有值。
少了「每一筆都有值」會怎樣?資料庫的唯一約束(UNIQUE)管的是有值的那些列;至於 NULL,各家的規則不一樣:
| 資料庫 | 唯一約束允許幾筆 NULL |
|---|---|
| Informix | 最多一筆1 |
| SQL Server | 一筆2 |
| Oracle | 單欄可以有多筆 NULL;複合鍵裡全部欄位都是 NULL 的列一律通過3 |
| PostgreSQL | 預設可以多筆;加上 NULLS NOT DISTINCT 才限制成一筆4 |
就算只允許一筆,問題也還在:NULL 用 = 比對永遠不成立。CDC 事件帶著 NULL 回來找那一列,找不到。(那個 NULL 到底是「沒有」還是「還沒填」,是另一個問題,〈資料庫用空白代替 NULL 會發生什麼事?〉那篇談過。)
資料庫理論裡有個現成的名詞叫「候選鍵」:能唯一認出每一列、而且沒有多餘欄位的欄位組合;主鍵是從候選鍵裡選出的一組。
但維度一我不用「候選鍵」這個名詞,直接寫成「唯一+NOT NULL」。原因出在理論本身:關聯式資料庫理論的奠基者 Codd 在 1971 年定義候選鍵時,只要求被選為主鍵的那一組欄位不可以是未定義值,其他欄位沒有這個要求5。照字面,允許 NULL 的候選鍵是存在的——但它不能拿來認出每一筆。
實務上的工具,挑鍵時要的正是這一條。Oracle 的補充記錄在沒有主鍵時,會去找「不可為空的唯一索引」6;PostgreSQL 要指定一個唯一索引當作複製時識別列的依據,那個索引的欄位必須全部是 NOT NULL7。
維度二:值會不會變
維度一只保證「此刻」。第二件事是:同一筆資料,明天還能用同一個值找到嗎?
這一點,Codd 自己在 1979 年就寫過。他指出由使用者決定、使用者控制的鍵,既然值是人決定的,就一定會被人改;他舉的例子是兩家公司合併,一部分員工編號因此被改掉8。把「公司合併」換成「會員合併」,就是開場那段對話。
Kimball 在 1998 年從資料倉儲的角度講了同一件事:來源系統的鍵,會依來源系統的需要被產生、改格式、更新、刪除、回收再利用9。
值一旦改變,開源的 CDC 工具 Debezium 不會把它當成一次更新,而是把改到主鍵的 UPDATE 拆成「舊鍵刪除」加上「新鍵新增」10。對下游來說,這是一個人消失、另一個人出現。
同樣是「會變」,變法不一樣。我把它分成三級:
| 級 | 值怎麼變 | 變的時候看不看得到 | 例子 |
|---|---|---|---|
| 不會變 | 系統產生,沒有人會去改 | — | 只給系統用的流水號 |
| 會變,看得到 | 業務事件:更正、併號 | 看得到:那是一筆 UPDATE 或 DELETE,追得到,外鍵也擋得下 | 身分證號、會員編號 |
| 會變,看不到 | 實體操作:更新時搬位置、重整 | 看不到:沒有任何一筆變更在記錄「位置換了」,空出來的位置還會被下一列拿去用 | rowid(第四節) |
第三級最危險:值變了,卻沒有任何東西告訴你。我用 PostgreSQL 內建的邏輯解碼實測過:VACUUM FULL 讓一列換了位置,交易日誌裡卻解不出任何一列的變更。

值變了會怎樣:一次會員合併
回頭看開場那次合併。鍵的值一變,後果不必等到接 CDC 才看得到。我在本機 PostgreSQL 把它拆成幾步重演一次,每一步都先寫下預期,再跑查詢核對。
步驟 0:起點。 小明下過四張訂單,但被重複註冊成兩個會員編號;小華下過一張。
會員表:
| 會員編號 | 姓名 |
|---|---|
| 0012345 | 小明 |
| 0098765 | 小明 |
| 0023456 | 小華 |
訂單表:
| 訂單編號 | 會員編號 | 日期 |
|---|---|---|
| 1 | 0012345 | 01-05 |
| 2 | 0012345 | 03-10 |
| 3 | 0098765 | 02-14 |
| 4 | 0098765 | 04-01 |
| 5 | 0023456 | 02-20 |
這時候用訂單表數人頭,會數出 3 人,但真實只有 2 人。這正是要合併的原因。
步驟 1:合併。 客服確認 0012345 和 0098765 是同一個人,保留 0012345,刪掉 0098765:
DELETE FROM 會員表 WHERE 會員編號 = '0098765';
會員表剩下 0012345、0023456 兩列。訂單表沒有人動過,第 3、4 筆的會員編號仍然是 0098765。
步驟 2:核對。
| 核對項目 | 預期 | 實際 |
|---|---|---|
| 小明有幾張訂單 | 4 | 2 |
| 5 筆訂單裡,對得到會員的有幾筆 | 5 | 3 |
| 用訂單表數人頭 | 2 | 3 |
對不到會員的,正是第 3、4 筆:它們掛在 0098765 底下,而 0098765 已經不在會員表裡了。合併原本是要把人數修正成 2,結果人數沒修好,還讓小明一半的訂單紀錄找不到主人。整個過程沒有任何錯誤訊息。
錯在哪一步? 合併其實是兩個動作:刪掉 0098765,以及把掛在 0098765 底下的訂單改掛到 0012345。後面那個動作就是「鍵的值變了」:第 3、4 筆訂單的會員編號,要從 0098765 變成 0012345。步驟 1 只做了前一半。
對照:訂單表有外鍵。 起點完全相同,唯一的差別是訂單表宣告了「會員編號必須對得到會員表」。同一句 DELETE 會當場被擋下,資料庫回報 0098765 仍被訂單表參照(原始訊息:violates foreign key constraint)。資料庫逼你先處理掛在 0098765 底下的訂單。正確的處理是把第 3、4 筆改掛到 0012345(更新 2 列),再刪 0098765。再核對一次,小明 4 張訂單、數人頭 2 人,兩項都符合預期。
但外鍵不保證資料是對的。 它擋下的是「指向不存在的人」,不是「指向錯的人」。從同一個起點,我又試了三種做法,外鍵全部放行:
| 做法 | 外鍵的反應 | 實跑結果 |
|---|---|---|
| 把 0098765 的訂單誤掛到 0023456(小華) | 放行,因為 0023456 存在 | 小明 2 張、小華 3 張,兩個人的紀錄都錯 |
| 為了讓 DELETE 過關,先刪掉 0098765 的訂單 | 放行 | 小明剩 2 張,另外兩筆訂單永遠消失 |
外鍵宣告成 ON DELETE CASCADE | 不擋,還替你把訂單一起刪掉 | 訂單從 5 筆變 3 筆,沒有任何錯誤訊息 |
外鍵保證每一筆訂單都找得到一個會員。它不知道那是不是對的人,也不知道該不該刪。宣告成 ON DELETE CASCADE 時,它甚至會替你把紀錄刪掉。它能做的,是在錯誤發生的那一刻逼你停下來;停下來之後做得對不對,還是要靠核對。
鍵的值變了,掛在它底下的歷史就斷了;沒有外鍵,斷了也沒有任何訊號。 外鍵做的事,是讓「紀錄找不到主人」這一種錯在發生的那一刻被擋下來。只有這一種。
二、主鍵(PK)是角色,代理鍵(Surrogate Key)是來源
到這裡,可以回頭看開場那段對話卡在哪裡。
「主鍵」「唯一鍵」說的是這把鍵的角色:資料庫上宣告了什麼。「自然鍵」「代理鍵」說的是值的來源:
- 自然鍵:值有業務意義,是人輸入或決定的,例如身分證號
- 代理鍵:系統產生、本身沒有意義的號碼,例如流水號(serial)
這兩組分類互相獨立,四種組合都存在:
| 自然鍵 | 代理鍵 | |
|---|---|---|
| 擔任主鍵 | 會員表的主鍵=身分證號 | 會員表的主鍵=系統流水號 |
| 不擔任主鍵 | 同一張表另設唯一鍵=身分證號 | 來源系統的流水號,帶進中台後成為一般欄位 |
維度一跟角色有關:主鍵天生就是唯一+NOT NULL,唯一鍵則要另外確認有沒有 NOT NULL。維度二跟角色無關,跟來源有關:一個被宣告成主鍵的身分證號,打錯字更正時照樣會變。
代理鍵是相對的
這裡有三個容易踩的地方。
第一,站在哪個系統看,答案不同。 來源系統的流水號,在來源系統裡是代理鍵;帶到中台之後,它是外部系統給的號碼,會不會被回收、會不會被重發,中台都管不到。Kimball 把這種來源系統的鍵叫做 production key9;在中台,它應該被當成自然鍵看待。
第二,系統發的號,一旦有人在用,就不再是代理鍵。 Codd 1979 年描述的代理鍵有一個常被忽略的條件:使用者可以讓系統產生或刪除它,但控制不了它的值,它的值也從不顯示給使用者8。會員編號是系統發的,但它印在會員卡上、被客服唸出來、被拿去查詢——人開始依賴它,它就有了業務意義,也就會像業務值一樣被合併、被更正。
所以會員編號不是代理鍵。它是系統發號的自然鍵。
第三,同樣叫代理鍵,代表的東西不一定一樣。 會員主檔的代理鍵代表一個人;SCD 那篇談過的維度表 customer_key,代表的是這個人的一個版本——同一個人搬了家,就多一列、多一個 customer_key。兩者都是系統產生、沒有業務意義,但一個跟著人走,一個跟著版本走。
前兩點合起來,就是我主張中台兩把鍵都留的原因:代理鍵給系統之間 JOIN,自然鍵給人查詢和對帳。代理鍵對業務使用者沒有意義,本來就不該讓他們看見;自然鍵讓人看得懂,但不能當成系統內部的身分。
三、判斷三問
遇到一把鍵,依序問三題:
- 角色:schema 上宣告成主鍵、唯一鍵,還是都沒有?唯一鍵的欄位是不是 NOT NULL?
- 來源:值是業務輸入,還是系統發號?
- 有沒有人在用它:人會不會看到、唸出、拿它查詢、要求更改?會的話,不論是誰產生的,都當自然鍵對待
第 1 題回答維度一,第 2、3 題回答維度二。
第 1 題查得到,但查得到的是宣告,不一定是強制。雲端資料倉儲就是例子:Snowflake、BigQuery 的主鍵都只宣告、不強制,BigQuery 的文件還提醒,約束被違反時,查詢可能回傳錯誤的結果1112。在這類環境裡,第 1 題要多問半句:宣告了,資料真的符合嗎?
第 2、3 題查不到,只能問人。這也是溝通時最好把三件事一起說的原因。與其說「這張表有主鍵」,不如說「會員表的主鍵是會員編號,系統發號,但會被合併」——角色、來源、有沒有人在用,一句話交代完。
四、那 rowid 呢?
還有一種常被拿來頂替的東西:rowid。
rowid 不是鍵,是位置:這一列現在放在磁碟上的哪一頁、哪一格。各家叫法不同(Oracle 的 ROWID、PostgreSQL 的 ctid、Informix 的 rowid),講的是同一類東西。
用兩個維度看它:此刻,每一列的位置確實不重複,所以它分得開兩列;但位置會變,變的時候沒有任何紀錄,而且會被重用,也就是維度二的第三級。我在 PostgreSQL 跑了 4 步:寫入、更新、刪除後重整、再新增,看每一步 rowid 怎麼變。
| 步驟 | 小明 | 小華 | 小美 | 小強 |
|---|---|---|---|---|
| 1. 寫入三筆 | (0,1) | (0,2) | (0,3) | — |
| 2. 更新小明的城市 | (0,4) | (0,2) | (0,3) | — |
| 3. 刪除小華,重整整張表 | (0,2) | — | (0,1) | — |
| 4. 新增小強 | (0,2) | — | (0,1) | (0,3) |
位置 (0,1) 一開始是小明的,第三步之後變成小美的;位置 (0,3) 原本是小美的,第四步給了小強。如果下游記住的是「(0,1)」,它找到的會是那個位置上現在住的人。
(PostgreSQL 的 ctid 每次更新都會換位置,Informix 的 rowid 在一般更新下多半不變13,兩者機制不同。這裡要示範的是「位置會被重用」這件事本身,不是數字。)
PostgreSQL 的官方文件直接寫了:ctid 會因更新或 VACUUM FULL 而改變,不應當作列的識別碼,要用主鍵14。Informix 的文件也建議用主鍵,而不是倚賴 rowid15。
Codd 在 1970 年那篇奠定關聯模型的論文,開頭就主張:使用者不該需要知道資料在機器裡是怎麼組織的16。照這個原則看,rowid 恰恰就是「資料在機器裡怎麼組織」。拿它當身分,等於把邏輯上的「這是誰」綁在實體上的「放在哪」。

五、代理鍵會不會比較慢?
先回答:查詢不會,寫入會。
一提到加一個流水號,常聽到兩種擔心:多一欄、多一個索引,會不會拖慢?整數鍵是不是一定比文字鍵快?我在本機的 PostgreSQL 16 把這兩件事分開量。
寫入:多一個索引,就多付一份維護。 中台兩把鍵都留,代理鍵就是多出來的那一個索引。一次寫入 100 萬筆:
| 設計 | 寫入時間 | 表加索引的大小 |
|---|---|---|
| 只有業務鍵(1 個索引) | 2.4~2.6 秒 | 80 MB |
| 代理鍵+業務鍵唯一(2 個索引) | 3.6~3.7 秒(多四到五成) | 101 MB(多約四分之一) |
這裡量的是一次整批寫入,對應中台批次載入的情境;來源系統那種一筆一筆的交易寫入,多一個索引的比例會不同,這次沒有量。
查詢:拿哪一種鍵來 JOIN,差別在寬度。 主表 10 萬筆、明細表 100 萬筆,三種鍵的 JOIN 結果完全相同:
| 整數 | 單欄文字 | 三欄複合 | |
|---|---|---|---|
| 主表主鍵索引 | 2.16 MB | 3.03 MB | 5.82 MB |
| 明細表外鍵索引 | 8.88 MB | 9.75 MB | 10.61 MB |
| 全表 JOIN 彙總 | 259 ms | 283 ms(+9%) | 371 ms(+43%) |
走索引查單筆,三者都在 0.1 毫秒內。整數只比單欄文字快 9%;真正貴的是三欄複合,慢了 43%。
所以代理鍵的代價落在寫入那一側:多一個索引,批次載入慢四到五成,空間多約四分之一。換到的是維度二,也就是明天還認得。如果你的業務鍵是三四個欄位拼起來的,代理鍵還能在查詢上把那 43% 賺回來;如果業務鍵本來就是單欄,選代理鍵就是花寫入成本買穩定,不是為了速度。這些數字只代表這台機器、這個資料量,要看的是方向。另外,流水號和 UUID 都是代理鍵;UUID 比整數寬,這次的實測只量了整數。
寬度之外,複合鍵還有第二個代價。候選鍵的定義要求不能有多餘的欄位:拿掉任何一欄,就不再唯一5。這不只是理論上的整潔——鍵裡每多一個欄位,就多一個可能被更正的值,維度二的風險跟著變大。
六、五種鍵的排序:兩個維度放在一起看
要靠一把鍵認人的地方,不管是 CDC 套用變更、中台的事實表 JOIN,還是 SCD 的版本表,要的都不只是此刻找得到那一列,還要在每一個時間點都找到同一列:維度一要過,維度二也要過。把兩個維度放在一起,就能把前面談過的五種鍵畫在同一張圖上。

排序時先比值的來源,再比宣告的角色:同樣是主鍵,代理鍵排在業務值前面。
| 名次 | 鍵 | 此刻認得準? | 明天還認得? | 值一變,會斷在哪裡 |
|---|---|---|---|---|
| 1 | 代理鍵當主鍵 | ✅ | ✅ 不會變 | 不會斷:值不會變 |
| 2 | 業務值當主鍵 | ✅ | ⚠️ 會變,看得到:更正、合併是一筆 UPDATE 或 DELETE | 掛在舊值上的紀錄會斷;有外鍵時,「找不到主人」會被擋下 |
| 3 | 業務值當 NOT NULL 唯一鍵 | ✅ | ⚠️ 同上 | 同上,但外鍵預設不參照它,要另外指定 |
| 4 | 允許 NULL 的唯一鍵 | ❌ NULL 那筆用 = 永遠對不到 | ⚠️ 同上 | NULL 那筆從一開始就認不出人 |
| 5 | rowid | 只保證這一刻 | ❌ 會變、看不到,還會被重用 | 位置被重用,改到別人也沒有訊號 |
幾點說明:
- 第 2 名排在第 3 名前面,不是因為主鍵的值比較不會變,而是主鍵預設就有保護:來源端預設記下主鍵的舊值7、CDC 工具預設先找主鍵6、外鍵預設參照主鍵17。唯一鍵要另外設定,才有同樣的保護。
- rowid 排最後:它落在維度二的第三級。允許 NULL 的唯一鍵,有值的那些列至少認得準;rowid 連有值的列,明天都可能換人,而且換的時候沒有任何紀錄
- 第 1 名有前提:這個號碼只給系統用,不讓人看、不讓人查。會員編號是系統發號,但印在會員卡上、被拿去查詢,所以屬於第 2 名。代理鍵也不是永遠不變:Codd 1979 有提到兩個資料庫合併時代理鍵可能要重發8;但那是系統層級的一次性工程,不是日常業務裡的更正與合併
回到開頭顧問的建議。把現有的唯一鍵升成主鍵,改的是角色:CDC 工具會優先用它、外鍵會參照它,在排序表上從第 3 名升到第 2 名。但值還是同一個業務值,維度二沒有改變,更正和合併照樣會讓它變。要走到第 1 名,得另外加一個流水號,代價是第五節量到的寫入成本。兩個做法不是誰對誰錯,而是花多少成本、買到哪一個維度。
唯一+NOT NULL 保證此刻認得準,代理鍵(Surrogate Key)保證明天還認得。
把這五種鍵真的接上 CDC 工具,會怎樣?〈數據中台同一筆變更,該套到哪一列?〉用開源的 Debezium 實跑:設定補齊之後,前三種鍵的同步結果都一致,差別只剩這篇講的這一層,也就是值會不會變。一張沒有主鍵的舊表值不值得補主鍵,也在那一篇。
參考資料
- HCL Informix 文件,CREATE INDEX:「A column with a unique index can have, at most, one NULL value.」https://informix.hcldoc.com/12.10/help/topic/com.ibm.sqls.doc/ids_sqs_0402.htm (查證日 2026-09-24) ↩
- Microsoft 官方文件,Unique constraints and check constraints:唯一約束只允許一個 NULL 值。https://learn.microsoft.com/en-us/sql/relational-databases/tables/unique-constraints-and-check-constraints (查證日 2026-09-24) ↩
- Oracle 官方文件,constraint:單欄唯一鍵可以包含 NULL;複合唯一鍵中全部欄位皆為 NULL 的列一律滿足約束。https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/constraint.html (查證日 2026-09-24) ↩
- PostgreSQL 官方文件,Constraints:唯一性比較中兩個 null 預設不視為相等,因此可存進多列 null;可用
NULLS NOT DISTINCT改變此行為。https://www.postgresql.org/docs/current/ddl-constraints.html (查證日 2026-09-24) ↩ - E. F. Codd,〈Further Normalization of the Data Base Relational Model〉, IBM Research Report RJ909, 1971, §1.3:定義候選鍵,並說明主鍵的欄位不可為未定義值、其他欄位可以。(轉錄版)https://thaumatorium.com/articles/the-papers-of-ef-the-coddfather-codd/1971b-further-normalization-of-the-data-base-relational-model/ (查證日 2026-09-24) ↩
- Oracle 官方文件,Database-Level Supplemental Logging:無主鍵但有一個以上不可為空的唯一索引鍵時,會挑其中一個作為識別該列的依據。https://docs.oracle.com/database/121/SUTIL/GUID-D2DDD67C-E1CC-45A6-A2A7-198E4C142FA3.htm (查證日 2026-09-19) ↩
- PostgreSQL 官方文件,ALTER TABLE,REPLICA IDENTITY:預設(DEFAULT)記錄主鍵欄位的舊值;USING INDEX 指定的索引必須「unique, not partial, not deferrable, and include only columns marked NOT NULL」;「If this index is dropped, the behavior is the same as NOTHING」。https://www.postgresql.org/docs/current/sql-altertable.html (查證日 2026-09-24) ↩
- E. F. Codd,〈Extending the Database Relational Model to Capture More Meaning〉, ACM TODS 4(4), 1979, §4, pp. 409–410:使用者控制的鍵「must therefore be subject to change by them」(例:兩家公司合併後員工編號被改);系統指派的代理鍵,使用者「have no control over its value, nor is its value ever displayed to them」。他也承認「Actually, under the covers, such surrogates may have to be changed (e.g., when two previously independent databases are combined into one)」。Codd 在文中註明,此概念部分來自 Hall, Owlett & Todd(1976)〈Relations and entities〉。 ↩
- Ralph Kimball,〈Surrogate Keys〉, Kimball Group, 1998-05-02:「Production keys such as product keys or customer keys are generated, formatted, updated, deleted, recycled, and reused according to the dictates of production.」https://www.kimballgroup.com/1998/05/surrogate-keys/ (查證日 2026-09-24) ↩
- Debezium 官方文件,PostgreSQL connector:「When a key changes, Debezium outputs three events: a DELETE event and a tombstone event with the old key for the row, followed by an event with the new key for the row.」https://debezium.io/documentation/reference/stable/connectors/postgresql.html (查證日 2026-09-24) ↩
- Snowflake 官方文件,Overview of Constraints:一般資料表的 PRIMARY KEY、UNIQUE、FOREIGN KEY 為「Optional, not enforced」,NOT NULL 為「Optional, enforced」;hybrid table 例外。https://docs.snowflake.com/en/sql-reference/constraints-overview (查證日 2026-09-24) ↩
- Google Cloud 官方文件,BigQuery primary and foreign keys:「BigQuery doesn't enforce primary and foreign key constraints.」「Queries over tables with violated constraints might return incorrect results.」https://docs.cloud.google.com/bigquery/docs/primary-foreign-keys (查證日 2026-09-24) ↩
- HCL Informix 文件,Definition of Rowid/forward pointer:「Every data row in a nonfragmented table is uniquely identified by an unchanging rowid.」資料列變長、原頁放不下而搬家時,原位置留下指向新位置的 forward pointer。https://help.hcl-software.com/hclinformix/1410/adr/ids_adr_0305.html (查證日 2026-09-24) ↩
- PostgreSQL 官方文件,System Columns:
ctid是該列版本在表中的實體位置;列被更新或被VACUUM FULL搬動時會改變,因此不應當作列的識別碼,應該用主鍵來識別邏輯上的列。https://www.postgresql.org/docs/current/ddl-system-columns.html (查證日 2026-09-20) ↩ - HCL Informix 文件,Using Rowids:文件建議使用主鍵作為存取方法,而不是倚賴 rowid 欄位。https://help.hcl-software.com/hclinformix/1410/sqs/ids_sqs_1440.html (查證日 2026-09-19) ↩
- E. F. Codd,〈A Relational Model of Data for Large Shared Data Banks〉, CACM 13(6), 1970, p. 377 摘要:「Future users of large data banks must be protected from having to know how the data is organized in the machine (the internal representation).」https://www.engineering.upenn.edu/~zives/03f/cis550/codd.pdf (查證日 2026-09-24) ↩
- PostgreSQL 官方文件,Constraints(Foreign Keys):「in absence of a column list the primary key of the referenced table is used as the referenced column(s).」https://www.postgresql.org/docs/current/ddl-constraints.html (查證日 2026-09-24;沙盒
pk_prequel_checks.sqlC4 實跑相符) ↩
延伸閱讀
如果你也走在自己的路上,歡迎訂閱《小穗步電子報》——我會把每一次的覺察與練習,寫成信寄給你。→ 訂閱小穗步電子報