Postgres 資料庫入門:建表與資料型別 | Supabase 完整教學
Supabase 的核心,其實就是一個完整的 PostgreSQL 資料庫。要用好 Supabase,第一步是學會如何在 Postgres 裡建立 table、選對資料型別、設定主鍵,並用 Table Editor 或 SQL Editor 存取資料。本篇帶你打好整個系列賴以運作的資料庫地基。
前言
一句話定義本篇主題:這篇要教你在 Supabase 的 Postgres 裡建立第一張資料表——選對欄位型別、設好主鍵、塞入資料、查出來——並學會 Table Editor 與 SQL Editor 這兩種操作方式。
上一篇《雲端 vs 自架與定價》我們完成了「平台總覽」的最後一塊:知道該用雲端還是自架、看懂定價方案,也親手建立了第一個雲端專案、走過一遍 Dashboard。從這一篇開始,我們要一頭鑽進 Supabase 的地基——Postgres 資料庫。
有件事要先建立正確認知:Supabase 不是「把資料庫包裝成某種受限 API」的服務,官方一再強調——每個 Supabase 專案拿到的是一個完整的 Postgres,而不是 Postgres 的閹割版。這代表你在任何 Postgres 教科書、Stack Overflow 上學到的 SQL,在 Supabase 裡都能原封不動地用。學會 Postgres,就等於學會了 Supabase 的一大半。
如果用現實世界的類比,一張資料表(table)就像一份試算表:每一欄(column)是一個固定用途的欄位(例如「標題」「建立時間」),每一列(row)是一筆完整的資料紀錄。差別在於,資料庫的每一欄都必須先講好它的資料型別(data type)——這一欄只能放文字、只能放整數、還是只能放時間——講好之後,資料庫就會幫你把關,擋掉不符規格的髒資料。這種「先定規格、再存資料」的嚴謹,正是資料庫比試算表可靠的地方。
本篇你會學到:
- table、column、row 的基本概念,以及「public schema」是什麼
- 常用資料型別:
text、int、uuid、timestamptz、jsonb、boolean各自的用途與取捨 - 主鍵(Primary Key)怎麼設,以及用
gen_random_uuid()產生 uuid 主鍵 - 兩種操作方式:Table Editor(視覺化)與 SQL Editor(寫 SQL),並實際跑一遍
create table/insert/select
核心概念
table、column、row 與 public schema
資料表(table)是 Postgres 儲存資料的基本單位。一張表由**欄(column,決定「有哪些欄位、各是什麼型別」)與列(row,每一筆實際資料)**組成。
在建立資料表之前,先認識一個容易被略過但很重要的詞:schema(綱要)。schema 是資料庫裡的「命名空間」,用來把資料表分門別類。Supabase 專案預設就有一個名為 public 的 schema,你透過 Table Editor 或 SQL Editor 建立的資料表,若沒特別指定,都會落在 public 底下。這也是為什麼 Supabase 自動產生的 API 預設會暴露 public schema 裡的資料表——它是「一般應用程式資料」的預設家。
除了 public,Supabase 還有幾個系統 schema,例如 auth(存放使用者帳號,不要手動改)、storage(檔案系統資料表)。這一篇我們只在 public 活動;自訂 schema、schema 之間的關聯與權限,留給後續文章。
-- 這兩種寫法是等價的,因為 public 是預設 schema
create table books ( ... );
create table public.books ( ... );
常用資料型別
建表時最需要用心的,就是幫每一欄選對資料型別。Postgres 的原生型別非常豐富,但入門階段掌握下面這六個就夠應付九成情境:
| 型別 | 用途 | 範例值 | 備註 |
|---|---|---|---|
text | 任意長度文字 | 'Supabase 教學' | 標題、內文、名稱都用它,不必用 varchar(n) |
int / bigint | 整數 | 42、100000 | 數量、分數;int 約 21 億上限,怕不夠就用 bigint |
boolean | 是非值 | true / false | 開關類欄位,如「是否公開」 |
timestamptz | 含時區的時間戳 | now() | 記錄時間一律用它,別用裸的 timestamp |
uuid | 通用唯一識別碼 | gen_random_uuid() | 全域唯一的 ID,常用作主鍵 |
jsonb | 二進位 JSON | '{"color":"red"}' | 存放彈性、半結構化的資料 |
幾個新手最該記住的取捨:
- 文字優先用
text:很多人從其他資料庫帶著「要用varchar(255)限制長度」的習慣,但在 Postgres,text沒有效能懲罰、也不佔額外空間,長度限制交給應用層或check約束處理即可。除非你真的有「這欄最多 N 字」的業務規則,否則直接用text最省事。 - 時間一律用
timestamptz:timestamptz(timestamp with time zone)會把時間以 UTC 儲存、依時區呈現,自動處理跨時區換算;裸的timestamp不記時區,跨地區就會出現「差幾小時」的難查 bug。 - JSON 用
jsonb而非json:jsonb以二進位格式儲存,支援索引、查詢更快;json只是原封不動存字串。除非你需要保留 JSON 原始格式(含空白與鍵順序),否則永遠選jsonb。
主鍵與 gen_random_uuid()
主鍵(Primary Key)是用來唯一識別資料表中每一列的欄位。一張正式的資料表幾乎都應該有主鍵——它保證「不會有兩筆一模一樣分不出來的資料」,也是其他資料表未來要「關聯」到這張表時的錨點。主鍵有兩種主流做法:
做法一:自動遞增整數(identity)。 由資料庫自動配一個遞增的整數當 ID。現代 Postgres 建議用 generated always as identity,而不是舊時代的 serial(原因見後面的「常見錯誤」)。
id bigint generated always as identity primary key
做法二:uuid 主鍵。 用 uuid 型別,搭配 Postgres 內建函式 gen_random_uuid() 當預設值,每插入一列就自動產生一個像 a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11 的全域唯一值。
id uuid default gen_random_uuid() primary key
gen_random_uuid() 是 Postgres 13 之後的內建函式,不需要額外啟用任何 extension(早期教學常見的 uuid_generate_v4() 來自 uuid-ossp extension,如今在多數情況已可用內建函式取代)。uuid 的好處是全域唯一、不可預測(不會像 1, 2, 3 那樣被人猜到、遍歷)、還能在客戶端先產生。Supabase 的 auth.users 表主鍵就是 uuid,所以你的使用者相關資料表常會跟著用 uuid 才好對接。
兩種操作方式:Table Editor vs SQL Editor
在 Supabase Dashboard 裡,你有兩種方式操作同一個 Postgres:
- Table Editor:圖形化、類試算表的介面。用點選建立資料表、新增欄位、直接改格子裡的值。適合快速建原型、手動檢視或修改少量資料、不想寫 SQL 的時候。
- SQL Editor:直接寫 SQL 執行,還能把常用查詢存成 Snippet。適合精確、可重複、需要版本控制、或一次要做多個動作的情境。
關鍵觀念是:兩者背後是同一個資料庫。你用 Table Editor 建的表,在 SQL Editor 用 SQL 一樣查得到;反之亦然。初學階段建議兩個都練——用 Table Editor 培養對「表長什麼樣」的直覺,用 SQL Editor 學會可攜、可版本控制的標準 SQL。這一篇的實作,我們主力用 SQL Editor(因為 SQL 更精確、好複製貼上),最後再帶你到 Table Editor 對照結果。
實作範例
我們來實際建一張「書籍」資料表 books,塞幾筆資料,再查出來。打開 Supabase Dashboard 左側的 SQL Editor,跟著逐段執行。
步驟一:建立資料表(CREATE TABLE)
先看完整的建表語句,下面再逐欄拆解:
-- 建立一張 books 資料表
create table books (
id uuid default gen_random_uuid() primary key,
title text not null,
author text,
price int not null default 0,
is_public boolean not null default false,
metadata jsonb default '{}',
created_at timestamptz not null default now()
);
逐欄說明它為什麼這樣寫:
id uuid default gen_random_uuid() primary key:用 uuid 當主鍵,沒指定值時自動用gen_random_uuid()產生。primary key同時代表這一欄不可重複、不可為空。title text not null:書名用text;not null表示這一欄必填,插入時漏掉會被資料庫擋下來。author text:作者也是文字,但沒加not null,所以可以留空(值會是null)。price int not null default 0:價格用整數;default 0表示插入時若沒給,就自動填 0。is_public boolean not null default false:是否公開,是非值用boolean,預設false。metadata jsonb default '{}':彈性欄位,用jsonb存半結構化資料,預設是空物件{}。created_at timestamptz not null default now():建立時間,用timestamptz並以now()自動填入當下時間——這是幾乎每張表都該有的欄位。
按下 Run,執行成功後 books 表就建好了。
步驟二:插入資料(INSERT)
接著塞入幾筆書籍。注意:我們不需要手動給 id 和 created_at——它們有預設值,會自動產生:
-- 插入一筆完整資料
insert into books (title, author, price, is_public)
values ('資料庫設計入門', '陳大文', 480, true);
-- 一次插入多筆
insert into books (title, author, price, is_public)
values
('Postgres 實戰', '林小美', 620, true),
('SQL 從零開始', '王阿明', 350, false);
-- 只給必填欄位,其餘吃預設值(price=0, is_public=false, metadata={})
insert into books (title)
values ('尚未定價的草稿');
-- 帶 jsonb 欄位的插入
insert into books (title, price, metadata)
values ('進階 SQL', 800, '{"level": "advanced", "pages": 320}');
這裡示範了幾種常見寫法:一次一筆、一次多筆(values 後面用逗號串接多組)、只給必填欄讓其餘吃預設值、以及寫入 jsonb。你會發現只要欄位設好了 default,插入時就能省很多力氣。
步驟三:查詢資料(SELECT)
資料塞進去了,來把它查出來。select 是最基本、也最常用的 SQL 動詞:
-- 查全部欄位、全部資料
select * from books;
-- 只選需要的欄位(正式程式碼建議這樣,別用 *)
select title, author, price from books;
-- 用 where 過濾:只看已公開的書
select title, price
from books
where is_public = true;
-- 用 order by 排序:依價格由高到低
select title, price
from books
where is_public = true
order by price desc;
-- 查詢 jsonb 欄位裡的值:->> 取出文字
select title, metadata ->> 'level' as level
from books
where metadata ->> 'level' = 'advanced';
逐段解讀:
select * from books:*代表「所有欄位」,快速檢視時很方便,但正式程式碼建議明確列出需要的欄位,省頻寬也更清楚。where is_public = true:where用來過濾,只回傳符合條件的列。order by price desc:排序,desc是由大到小,asc(預設)則由小到大。metadata ->> 'level':->>是 Postgres 存取jsonb內某個鍵、並以文字回傳的運算子——這就是jsonb比純文字強大的地方,你能直接對 JSON 內部欄位下條件。
步驟四:回到 Table Editor 對照
切換到左側的 Table Editor,點選 books 表,你會看到剛剛用 SQL 插入的所有資料整齊地排在表格裡,欄位型別、預設值都與你建表時設定的一致。你也可以直接在這裡點某個格子改值、或按「Insert row」用表單新增——這些視覺化操作,本質上只是 Supabase 幫你把等價的 SQL 送給同一個 Postgres 執行。這正好印證了核心觀念:Table Editor 與 SQL Editor 只是同一個資料庫的兩個門面。
到這裡,你已經完整走過:建表(含型別、主鍵、預設值)→ 插入 → 查詢 → 視覺化對照。這就是每天使用 Supabase 資料庫的基本節奏。
常見錯誤與最佳實踐
初次建表,最常踩的幾個坑:
坑一:主鍵用 serial 而不是 identity 或 uuid。
很多舊教學會寫 id serial primary key。serial 能動,但它是 Postgres 的舊式語法,背後其實是偷偷建一個 sequence,權限與所有權管理較麻煩,也不符合 SQL 標準。正確做法:需要遞增整數主鍵時,用 id bigint generated always as identity primary key;需要全域唯一 ID 時,用 id uuid default gen_random_uuid() primary key。兩者都比 serial 現代、乾淨。
坑二:時間欄位用 timestamp 而不是 timestamptz,沒帶時區。
用裸的 timestamp 存時間,資料庫不知道那是哪個時區的時間,一旦你的使用者或伺服器跨時區,就會出現「時間差了 8 小時」這種極難追查的 bug。正確做法:所有時間欄位一律用 timestamptz,並搭配 default now() 自動記錄。這是幾乎沒有例外的鐵律。
坑三:忘記設主鍵。
Postgres 允許你建一張沒有主鍵的表,但這是個陷阱——沒有主鍵,你就無法穩定地唯一定位某一列,未來要更新/刪除特定資料、或讓別的表關聯過來都會很痛苦,某些工具(包含 Supabase 的即時同步、部分編輯功能)也需要主鍵才能正常運作。正確做法:每張正式資料表都設一個主鍵,id uuid default gen_random_uuid() primary key 是很好的通用預設。
坑四:文字一律用 varchar(n) 並糾結長度。
從 MySQL 等資料庫轉來的人常習慣 varchar(255)。在 Postgres 這沒必要——text 不會比較慢也不會比較佔空間,還免去「當初長度開太小、後來要改」的麻煩。正確做法:預設用 text;真有長度上限的業務規則,用 check (char_length(col) <= n) 明確表達。
坑五:用 json 而不是 jsonb。
json 會把整段 JSON 當字串原樣存,查詢時每次都要重新解析、也無法建立高效索引。正確做法:除非你有保留原始格式的特殊需求,彈性欄位一律用 jsonb。
最佳實踐總結:
- 主鍵用
uuid + gen_random_uuid()或bigint generated always as identity,別用serial - 時間欄位一律用
timestamptz+default now(),不要用裸的timestamp - 文字預設用
text,別無腦套varchar(n) - 彈性/半結構化資料用
jsonb,別用json - 每張正式資料表都設主鍵;正式專案的 schema 盡量用 SQL(migration)管理,才能被 Git 追蹤與重現
- 善用
default(如now()、0、false、'{}')減少 insert 時的樣板
小結
這篇是 Supabase 系列教學的第 004 篇,也是「資料庫」章節的開篇。承接上一篇《雲端 vs 自架與定價》——我們已經建好雲端專案、認識了 Dashboard,這篇就正式走進 Supabase 的地基 Postgres,學會了最核心的建表與存取:
- 基本結構:table(表)由 column(欄,決定型別)與 row(列,實際資料)組成,預設都放在
publicschema - 常用資料型別:
text(文字)、int/bigint(整數)、boolean(是非)、timestamptz(含時區時間)、uuid(唯一 ID)、jsonb(彈性 JSON),並記住「text 優於 varchar、timestamptz 優於 timestamp、jsonb 優於 json」 - 主鍵:用
uuid default gen_random_uuid()或bigint generated always as identity,別用過時的 serial - 兩種操作:Table Editor(視覺化)與 SQL Editor(寫 SQL)背後是同一個資料庫;實作跑過了
create table/insert/select的完整流程
現在你已經會建一張獨立的資料表了。但真實的應用資料很少是孤立的——書有作者、訂單有商品、貼文有使用者。這些資料表之間的關聯(relationship),才是資料庫設計的精髓所在。
下一篇《Schema 設計與關聯》,我們會接著講外鍵(Foreign Key)、一對多與多對多關係、以及 on delete 的行為,教你把多張資料表正確地串起來,設計出經得起成長的資料模型。把單張表學會之後,是時候讓它們互相認識了。下一篇見。