### [Connecting to Postgresql in a docker container from outside](https://stackoverflow.com/questions/37694987/connecting-to-postgresql-in-a-docker-container-from-outside)
```
psql postgresql://postgres:1234@localhost:5432/postgres
```
```bash
# 連接到資料庫:使用以下指令連接到 PostgreSQL 資料庫
psql -U 用戶名 -d 資料庫名 -h 主機名 -p 連接埠
\l # 列出所有 PostgreSQL 中的資料庫
# 切換資料庫
\c 資料庫名
# 查看資料庫中的 Table
\d # 用於顯示當前資料庫中所有關聯對象(包括表格、視圖、索引、序列等)的命令
\dt # 僅顯示當前資料庫中的所有表格(關聯表)
\du # 列出 PostgreSQL 中的所有使用者
\d+ 顯示資料表的描述 (表結構)
```
### SQL
```sql
-- 取得最靠近現在時間的五筆資料
SELECT *
FROM your_table
ORDER BY ABS(EXTRACT(epoch FROM (your_timestamp_column - NOW())))
LIMIT 5;
-- 使用 EXTRACT(epoch FROM (your_timestamp_column - NOW())) 來計算每筆資料的時間差,以秒為單位。
-- LIMIT 5 限制結果只返回前五筆資料
```
### char(5) to varchar(5)
```sql
-- Step 1: 建立新的 varchar(5)欄位
ALTER TABLE order_list ADD COLUMN currency_new varchar(5);
-- Step 2: 複製數據至新欄位
UPDATE order_list SET currency_new = currency;
-- Step 3: 刪除原始欄位
ALTER TABLE order_list DROP COLUMN currency;
-- Step 4: 重新命名新欄位
ALTER TABLE order_list RENAME COLUMN currency_new TO currency;
```
### 使用者配置
```sql
-- 列出 PostgreSQL 中的所有使用者
SELECT usename FROM pg_user;
-- 執行以下命令來創建一個新的使用者
CREATE USER username WITH PASSWORD 'password';
-- 請將 `username` 更換為你想要的使用者名稱,將 `password` 更換為你想要設置的密碼。
-- 新增一個資料庫
CREATE DATABASE dbname;
-- 授予使用者適當的權限,例如讓使用者擁有對特定資料庫的訪問權限
GRANT ALL PRIVILEGES ON DATABASE database_name TO username;
-- 請將 `database_name` 更換為你想要給予存取權限的資料庫名稱。
-- 完成後,可以使用以下命令檢查新建使用者是否成功添加
\du
-- 移除某個使用者對某個資料庫的權限
REVOKE ALL PRIVILEGES ON DATABASE database_name FROM username;
-- 請將 `database_name` 替換為欲移除權限的資料庫名稱,將 `username` 替換為欲移除權限的使用者名稱。
```
# pg_dump 筆記
## 先搞懂它在幹嘛
`pg_dump` 本質上是把一個資料庫「拍照」下來,拍出來的照片格式可以是純文字 SQL、也可以是壓縮過的自訂格式。選哪種格式直接決定了你之後能不能做「選擇性還原」(只還原某幾張表、平行還原),所以格式選擇比想像中重要。
## 最常用的三種輸出格式
```bash
# 1. 純文字 SQL(-Fp,預設值,可省略)
pg_dump -h localhost -U postgres -d mydb -Fp -f mydb.sql
# 2. 自訂格式(-Fc):壓縮過,且支援 pg_restore 做選擇性/平行還原,最推薦
pg_dump -h localhost -U postgres -d mydb -Fc -f mydb.dump
# 3. 目錄格式(-Fd):每張表拆成獨立檔案,搭配 -j 可以平行匯出,適合超大資料庫
pg_dump -h localhost -U postgres -d mydb -Fd -j 4 -f mydb_dir
```
純文字格式的缺點是:它就是一份完整的 SQL 腳本,只能整份從頭跑到尾,想跳過某張表或平行處理都做不到。自訂格式(`-Fc`)則是把資料跟結構分開存,`pg_restore` 可以只挑你要的部分還原,還能開多個 worker 平行寫入,幾乎沒有理由不選這個,除非你就是想要一份人眼可讀、能直接用 `psql` 執行的 SQL 檔。
## 常搭配的參數
| 參數 | 作用 | 什麼時候用 |
|---|---|---|
| `-s` / `--schema-only` | 只匯出結構,不含資料 | 想先在新環境建好空的 schema 再灌資料時 |
| `-a` / `--data-only` | 只匯出資料,不含結構 | 結構已經建好,只想搬資料 |
| `-t table_name` | 只匯出指定表(可重複多次) | 只想備份某幾張表,不要整庫 |
| `-n schema_name` | 只匯出指定 schema | 多 schema 資料庫只想搬一部分 |
| `--no-owner` | 不記錄物件擁有者(角色) | 目標環境的角色名稱跟來源不同時,避免還原時因角色不存在而報錯 |
| `-j N` | 平行匯出 N 個 worker(只有 `-Fd` 支援) | 資料量大、想縮短匯出時間 |
## 全庫層級:pg_dumpall
`pg_dump` 只匯出「一個資料庫」,不包含角色(users/roles)和 tablespace 這些叢集層級的東西。如果是整個 PostgreSQL 叢集要搬家(例如版本升級),通常會搭配 `pg_dumpall`:
```bash
# 只匯出角色定義和權限,不含每個資料庫的實際資料
pg_dumpall -h localhost -U postgres --globals-only -f globals.sql
```
先用這個補上角色,再用 `pg_dump -Fc` 一個個把資料庫內容匯出,是版本升級時常見的組合。
---
# pg_restore 筆記
## 先搞懂它在幹嘛
`pg_restore` 只吃 `pg_dump` 用 `-Fc` 或 `-Fd` 匯出的檔案(純文字 `-Fp` 的檔案要直接用 `psql` 執行,不能用 `pg_restore`)。它做的事情是把那份「照片」讀出來,重新在目標資料庫建結構、灌資料。
## 基本流程
```bash
# 1. 先在目標端建一個空資料庫
createdb -h localhost -U postgres newdb
# 2. 把備份還原進去
pg_restore -h localhost -U postgres -d newdb mydb.dump
```
## 常搭配的參數
```bash
# 平行還原,4 個 worker 同時跑(只有 -Fc/-Fd 支援,大幅縮短還原時間)
pg_restore -h localhost -U postgres -d newdb -j 4 mydb.dump
# 還原前先清掉目標資料庫裡已存在的物件(避免「物件已存在」的報錯)
pg_restore -h localhost -U postgres -d newdb --clean --if-exists mydb.dump
# 忽略擁有者資訊,用目前連線的角色當擁有者(跨環境還原常用)
pg_restore -h localhost -U postgres -d newdb --no-owner mydb.dump
# 只還原結構,不還原資料
pg_restore -h localhost -U postgres -d newdb --schema-only mydb.dump
# 只還原某一張表
pg_restore -h localhost -U postgres -d newdb -t orders mydb.dump
```
## 選擇性還原的實用技巧
自訂格式(`-Fc`)備份可以先用 `-l` 印出裡面所有物件的清單,挑出你要的部分,再用 `-L` 指定只還原清單裡列的項目:
```bash
# 列出備份檔裡有哪些物件
pg_restore -l mydb.dump > contents.list
# 編輯 contents.list,刪掉不想還原的行,再指定回去
pg_restore -h localhost -U postgres -d newdb -L contents.list mydb.dump
```
這個做法比每次都用 `-t` 一張張指定表名方便很多,尤其是想跳過某些索引或觸發器,又不想動到主要表資料的時候。
## 常見報錯的排解方向
還原時最常撞到的是「物件已存在」跟「角色不存在」兩類錯誤:前者通常是目標資料庫不是全新的,加 `--clean --if-exists` 通常能解;後者是來源跟目標的角色設定不同,加 `--no-owner` 就能繞過(權限之後再手動補)。`pg_restore` 預設遇到錯誤會印出來但繼續往下跑,不會整個中斷,所以還原完務必看一下輸出訊息裡有沒有紅字。
---
## 版本升級
PostgreSQL 大版本升級不能直接掛載 volume 就跑,因為 on-disk 格式不相容。
### pg_dump / pg_restore(邏輯備份,推薦)
最穩、跨版本相容性最好,13→18 沒有官方支援 `pg_upgrade` 直接跳版(通常只保證 n → n+1,你這邊建議走邏輯匯出匯入)。
```bash
# 從舊的 postgres 13 匯出
pg_dumpall -h <new-host> -p <new_port> -U postgres > full_backup.sql
# 或針對單一 DB 用 custom format,體積小、可平行還原
pg_dump -h <new-host> -p <new_port> -U postgres -Fc your_db > your_db.dump
```
還原到新的 18.6:
```bash
pg_restore -h <new-host> -p <new_port> -U postgres -d your_db --no-owner --no-acl -j 4 your_db.dump
```
注意事項:
- `pg_dump` 用**新版**的 client(18.x 的 `pg_dump`/`pg_restore`)去讀舊的 13 資料庫比較保險,反向不保證相容。
- 檢查 extension 版本(尤其如果有用 PostGIS、pgcrypto 等),18.6 上要先 `CREATE EXTENSION` 對應版本。
- 檢查已棄用語法/型別(13→18 之間有些行為變更,例如 `WITH OIDS` 已完全移除、部分 collation 行為改變)。
---
# PostgreSQL:Schema 與 Owner 筆記
## 核心比喻:樓層編號 vs. 房間登記
資料庫裡最容易搞混的兩個字,就是「schema」跟「owner」。想像一棟大樓:
- **schema** 是樓層/房間編號,決定一個物件「放在哪裡」,純粹是命名空間、分類用途。
- **owner** 是房間登記在誰名下,決定「誰對這個物件有預設的完整權限」。
這兩件事在概念上完全獨立——同一個 schema 底下,可以有好幾張表分屬不同 owner;一張表也可以換 owner,而不需要搬到別的 schema。之所以會搞混,通常是因為 Oracle、MySQL 這類系統把兩者綁在一起,而 PostgreSQL 是刻意把它們拆開的少數幾套系統之一。
## 命名層級:schema 是一層,不是巢狀的
`xxx.table_name` 這種寫法裡,`xxx` 是 schema,不是 owner。schema 本身只有一層,不能再往下開子 schema。完整定位一張表理論上是三段式:`database.schema.table`,但實務上你不會真的寫出 database 那段——因為 PostgreSQL 一次連線只能連一個資料庫,SQL 語句裡也不允許跨資料庫查詢,想跨庫得靠 `postgres_fdw`/`dblink` 這類擴充功能,而不是靠命名路徑。
## 權限階層:上層是門檻,不是所有權的延伸
schema 和 owner 各自獨立,但**權限**確實有上下階層關係:
- 要在某個資料庫裡建 schema,得先對這個資料庫有 `CREATE` 權限(db owner 天生有,其他人要 `GRANT`)。
- 要在某個 schema 裡建表,得先對這個 schema 有 `CREATE` 權限(schema owner 天生有)。
也就是說,db owner 對底下所有 schema 都有建立東西的權限,但**不會自動變成**每個 schema 的 owner——這正是「所有權分層」的重點:讓大團隊裡不同小組能各自管好自己的 schema,不需要互相踩線,也不用事事驚動最上層的 DBA。
## Owner 實際能做什麼
物件(表、schema、sequence、function 都算)的 owner,不用額外 `GRANT` 就天生擁有:
- `ALTER`、`DROP` 這個物件
- 把權限 `GRANT` 給別人,或 `REVOKE` 收回
- 對表來說,預設就能 `SELECT`/`INSERT`/`UPDATE`/`DELETE`
沒有 owner 身分、也沒被授權的角色,連看內容都不行,即使能登入資料庫也一樣。
## 查詢語法
```
-- psql 互動指令(最快,背後也是查系統目錄,只是包裝成好讀的表格)
\dt+ table_name -- 列出表,含 Schema、Owner 欄位
\d+ table_name -- 看單一表詳細資訊,底部標 Owner
```
```sql
-- SQL 標準寫法:只查得到 schema,查不到 owner
-- (owner 是 PostgreSQL 專屬擴充,標準 information_schema 沒有這個概念)
SELECT table_schema, table_name
FROM information_schema.tables
WHERE table_name = 'orders';
-- PostgreSQL 原生目錄,資訊最完整
SELECT
n.nspname AS schema,
c.relname AS table_name,
pg_get_userbyid(c.relowner) AS owner
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = 'orders'
AND c.relkind = 'r'; -- 'r' = 一般表;'i'=索引 'v'=view 'm'=materialized view 'S'=sequence
-- 懶人版:pg_tables 幫你把上面那串 join 包好了
SELECT schemaname, tablename, tableowner
FROM pg_tables
WHERE schemaname NOT IN ('pg_catalog', 'information_schema');
```
`relowner` 在 `pg_class` 裡存的是角色的 oid(數字),要靠 `pg_get_userbyid()` 轉成名字,這是唯一比較容易忘記的細節。
## 跟其他資料庫系統的對照
| 資料庫 | schema 與 owner 的關係 |
|---|---|
| Oracle(及 SQL Server 2005 前) | schema = user,建立時綁死,無法拆分 |
| PostgreSQL、SQL Server 2005+ | schema 與 owner 解耦,可任意指派、轉讓 |
| MySQL | schema 只是 database 的別名,沒有獨立的 owner 概念 |
Oracle 是把「owner」這個角色直接做成了「schema」本身——建一個 user 就自動生出同名 schema,這個 user 建的所有東西永遠只能待在自己的 schema 底下,兩者連拆開想都沒有意義。這也是為什麼從 Oracle/MySQL 背景轉過來的人,第一次看到 PostgreSQL 的 `xxx.table_name` 很容易直覺當成 owner——舊系統的訓練直覺剛好在這裡失效。