前面提到了 supabase 用的是 possgreSQL,這兩天就來整理 possgreSQL 的相關基礎知識。
資料庫(Database)是一個有組織地儲存和管理資料的集合,就像是一個電子化的檔案櫃。
為什麼需要資料庫?
生活中的例子:
資料表(Table)是資料庫中儲存資料的基本單位,可以想像成 Excel 的工作表。
資料表的組成:
範例:使用者資料表
id | name | age | |
---|---|---|---|
1 | Alice | alice@example.com | 25 |
2 | Bob | bob@example.com | 30 |
3 | Charlie | charlie@example.com | 28 |
資料類型(Data Type)定義了欄位可以儲存什麼樣的資料,就像變數的型別。
為什麼需要定義資料類型?
常見的資料類型分類:
分類 | 類型 | 說明 | 範例 |
---|---|---|---|
數字 | INTEGER | 整數 | 25, -100 |
數字 | DECIMAL | 精確小數 | 99.99, 12.50 |
文字 | VARCHAR | 可變長度字串 | "Hello", "Alice" |
文字 | TEXT | 長文字 | 文章內容、描述 |
日期時間 | DATE | 日期 | 2024-01-15 |
日期時間 | TIMESTAMP | 日期+時間 | 2024-01-15 14:30:00 |
布林 | BOOLEAN | 真/假 | true, false |
一個完整的資料庫系統由三個主要部分組成:
1. 資料庫(Database)
blog
、users
、products
等資料庫2. 資料庫管理系統(DBMS - Database Management System)
3. 資料庫應用程式(Database Application)
系統架構圖:
使用者
↓
應用程式(React、Django、Express)
↓
DBMS(PostgreSQL)
↓
資料庫(實際的資料檔案)
SQL(Structured Query Language,結構化查詢語言)是用來與資料庫溝通的標準語言。
SQL 的主要分類:
1. DDL(Data Definition Language)- 資料定義語言
定義資料庫結構
CREATE TABLE users (...); -- 建立資料表
ALTER TABLE users ...; -- 修改資料表
DROP TABLE users; -- 刪除資料表
2. DML(Data Manipulation Language)- 資料操作語言
操作資料內容
INSERT INTO users ...; -- 新增資料
SELECT * FROM users; -- 查詢資料
UPDATE users SET ...; -- 更新資料
DELETE FROM users ...; -- 刪除資料
3. DCL(Data Control Language)- 資料控制語言
管理權限和存取控制
GRANT SELECT ON users TO john; -- 授予權限
REVOKE SELECT ON users FROM john; -- 撤銷權限
4. TCL(Transaction Control Language)- 交易控制語言
管理交易
BEGIN; -- 開始交易
COMMIT; -- 提交交易
ROLLBACK; -- 回滾交易
SQL 的特點:
應用程式需要透過特定的技術來連接和操作資料庫。
1. ODBC(Open Database Connectivity)
2. JDBC(Java Database Connectivity)
// JDBC 連接範例
Connection conn = DriverManager.getConnection(
"jdbc:postgresql://localhost:5432/mydb",
"username",
"password"
);
3. 現代的連接方式
ORM(Object-Relational Mapping)框架
// Prisma ORM 範例
const user = await prisma.user.create({
data: {
email: 'alice@example.com',
name: 'Alice',
age: 25
}
});
原生驅動程式
psycopg2
、asyncpg
pg
、node-postgres
pgx
// Node.js pg 套件範例
const { Client } = require('pg');
const client = new Client({
host: 'localhost',
port: 5432,
database: 'mydb',
user: 'myuser',
password: 'mypassword'
});
await client.connect();
const result = await client.query('SELECT * FROM users');
4. REST API / GraphQL
選擇建議:
PostgreSQL 是一個開源的關聯式資料庫管理系統(RDBMS),以穩定性、擴展性和標準遵循性聞名。
特性 | PostgreSQL | MySQL | MongoDB |
---|---|---|---|
類型 | 關聯式資料庫 | 關聯式資料庫 | NoSQL 文件資料庫 |
ACID 支援 | ✅ 完整 | ✅ 完整 | ⚠️ 部分 |
JSON 支援 | ✅ JSONB(高效能) | ⚠️ 基本 | ✅ 原生 |
複雜查詢 | ✅ 強大 | ✅ 良好 | ⚠️ 有限 |
開源授權 | ✅ MIT-style | ⚠️ GPL | ✅ SSPL |
適合場景 | 企業應用、複雜查詢 | Web 應用、簡單查詢 | 非結構化資料 |
PostgreSQL 的優勢:
最簡單的方式是使用 Docker:
# 拉取 PostgreSQL 映像檔
docker pull postgres:16
# 啟動 PostgreSQL 容器
docker run --name my-postgres \
-e POSTGRES_PASSWORD=mysecretpassword \
-e POSTGRES_USER=myuser \
-e POSTGRES_DB=mydb \
-p 5432:5432 \
-d postgres:16
參數說明:
--name
: 容器名稱-e POSTGRES_PASSWORD
: 資料庫密碼-e POSTGRES_USER
: 資料庫使用者(預設為 postgres)-e POSTGRES_DB
: 預設建立的資料庫名稱-p 5432:5432
: 將容器的 5432 port 映射到本機建立 docker-compose.yml
:
version: '3.8'
services:
postgres:
image: postgres:16
container_name: my-postgres
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mysecretpassword
POSTGRES_DB: mydb
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
啟動:
docker-compose up -d
# 進入容器
docker exec -it my-postgres psql -U myuser -d mydb
# 看到以下提示代表成功
mydb=#
下載:https://www.pgadmin.org/download/
連線設定:
下載:https://dbeaver.io/download/
支援多種資料庫,介面友善,完全免費。
-- 建立新資料庫
CREATE DATABASE blog;
-- 切換資料庫
\c blog
-- 建立 users 資料表
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
name VARCHAR(100) NOT NULL,
age INTEGER,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
欄位說明:
SERIAL
: 自動遞增的整數PRIMARY KEY
: 主鍵,唯一識別每一列VARCHAR(n)
: 可變長度字串,最多 n 個字元UNIQUE
: 欄位值必須唯一NOT NULL
: 欄位不可為空DEFAULT
: 預設值-- 新增單筆資料
INSERT INTO users (email, name, age)
VALUES ('alice@example.com', 'Alice', 25);
-- 新增多筆資料
INSERT INTO users (email, name, age)
VALUES
('bob@example.com', 'Bob', 30),
('charlie@example.com', 'Charlie', 28),
('david@example.com', 'David', 35);
-- 查詢所有資料
SELECT * FROM users;
-- 查詢特定欄位
SELECT name, email FROM users;
-- 條件查詢
SELECT * FROM users WHERE age > 25;
-- 多重條件
SELECT * FROM users
WHERE age > 25 AND name LIKE 'A%';
-- 排序
SELECT * FROM users ORDER BY age DESC;
-- 限制筆數
SELECT * FROM users LIMIT 2;
-- 組合使用
SELECT name, age FROM users
WHERE age >= 25
ORDER BY age ASC
LIMIT 3;
常用運算子:
=
: 等於>
, <
, >=
, <=
: 大於、小於!=
或 <>
: 不等於LIKE
: 模糊比對(%
代表任意字元)IN
: 在某個集合中BETWEEN
: 在某個範圍內-- 更新特定資料
UPDATE users
SET age = 26
WHERE email = 'alice@example.com';
-- 更新多個欄位
UPDATE users
SET name = 'Bob Smith', age = 31
WHERE email = 'bob@example.com';
-- 批次更新
UPDATE users
SET age = age + 1
WHERE age < 30;
-- 刪除特定資料
DELETE FROM users
WHERE email = 'david@example.com';
-- 刪除所有資料(危險)
DELETE FROM users;
-- 更安全的做法:加上條件
DELETE FROM users
WHERE created_at < NOW() - INTERVAL '1 year';
CREATE TABLE products (
id SERIAL, -- 自動遞增整數(1, 2, 3...)
quantity INTEGER, -- 整數(-2147483648 到 2147483647)
price DECIMAL(10, 2), -- 精確小數(總共10位,小數2位)
views BIGINT, -- 大整數
rating REAL -- 浮點數
);
選擇建議:
INTEGER
DECIMAL
或 NUMERIC
(避免浮點數誤差)BIGINT
CREATE TABLE posts (
id SERIAL,
title VARCHAR(200), -- 可變長度,最多200字元
content TEXT, -- 不限長度的文字
slug CHAR(10) -- 固定長度,不足補空白
);
選擇建議:
VARCHAR(n)
TEXT
CHAR
較少用,除非真的需要固定長度CREATE TABLE events (
id SERIAL,
event_date DATE, -- 日期(2024-01-15)
event_time TIME, -- 時間(14:30:00)
created_at TIMESTAMP, -- 日期+時間
updated_at TIMESTAMPTZ -- 日期+時間+時區(推薦)
);
-- 插入範例
INSERT INTO events (event_date, event_time, created_at, updated_at)
VALUES
('2024-12-25', '18:00:00', NOW(), NOW());
選擇建議:
DATE
TIMESTAMP
TIMESTAMPTZ
(強烈推薦)CREATE TABLE users (
id SERIAL,
email VARCHAR(255),
is_active BOOLEAN DEFAULT true,
is_verified BOOLEAN DEFAULT false
);
-- 查詢範例
SELECT * FROM users WHERE is_active = true;
SELECT * FROM users WHERE NOT is_verified;
-- 啟用 UUID 擴充
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE TABLE sessions (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id INTEGER,
token VARCHAR(255)
);
-- 插入時自動產生 UUID
INSERT INTO sessions (user_id, token)
VALUES (1, 'abc123');
使用場景:
CREATE TABLE user_settings (
id SERIAL PRIMARY KEY,
user_id INTEGER,
preferences JSONB
);
-- 插入 JSON 資料
INSERT INTO user_settings (user_id, preferences)
VALUES (1, '{"theme": "dark", "language": "zh-TW", "notifications": true}');
-- 查詢 JSON 欄位
SELECT preferences->>'theme' as theme FROM user_settings;
-- 條件查詢
SELECT * FROM user_settings
WHERE preferences->>'language' = 'zh-TW';
-- 更新 JSON 欄位
UPDATE user_settings
SET preferences = jsonb_set(preferences, '{theme}', '"light"')
WHERE user_id = 1;
JSONB vs JSON:
JSON
: 儲存原始文字,快速寫入JSONB
: 二進位格式,查詢更快(推薦)-- 查看所有資料表
\dt
-- 查看資料表結構
\d users
-- 查看資料庫列表
\l
-- 切換資料庫
\c database_name
-- 離開
\q
-- 執行 SQL 檔案
\i /path/to/file.sql
-- 清空資料表
TRUNCATE TABLE users;
-- 刪除資料表
DROP TABLE users;
-- 刪除資料庫
DROP DATABASE blog;
\timing on
SELECT * FROM users;
-- 顯示:Time: 0.234 ms
\x on -- 垂直顯示
SELECT * FROM users WHERE id = 1;
\x off -- 恢復水平顯示
# 匯出整個資料庫
docker exec my-postgres pg_dump -U myuser mydb > backup.sql
# 匯入資料
docker exec -i my-postgres psql -U myuser mydb < backup.sql