Skip to content

如何安裝及使用 goose 管理資料庫版本

Published: at 下午11:57

如何安裝及使用 goose 管理資料庫版本

安裝goose

linux

記得要有sudo 權限的帳號

curl -fsSL https://raw.githubusercontent.com/pressly/goose/master/install.sh |sh

指令

goose --help
 goose create  init sql 

就會產生一組時間和檔案名稱的sql檔案

 2026/02/11 08:00:10 Created new file: 20260211080010_init.sql

內容會如下

-- +goose Up
-- +goose StatementBegin
SELECT 'up SQL query';
-- +goose StatementEnd

-- +goose Down
-- +goose StatementBegin
SELECT 'down SQL query';
-- +goose StatementEnd

主要 goose Up 當 up 時 執行 goose StatementBegin 與 goose StatementEnd 之前的 sql

主要 goose Down 當 down 時 執行 goose StatementBegin 與 goose StatementEnd 之前的 sql

 goose create  init go

就會產生一組時間和檔案名稱的go檔案

 2026/02/11 08:00:10 Created new file: 20260211080010_init.go

內容如下

package migrations

import (
        "context"
        "database/sql"
        "github.com/pressly/goose/v3"
)

func init() {
        goose.AddMigrationContext(upInit, downInit)
}

func upInit(ctx context.Context, tx *sql.Tx) error {
        // This code is executed when the migration is applied.
        return nil
}

func downInit(ctx context.Context, tx *sql.Tx) error {
        // This code is executed when the migration is rolled back.
        return nil
}

使用範例

建立一組 up

CREATE TABLE public.flower_prod_day_trans (
	trans_date date NOT NULL,
	trans_date_roc text NULL,
	query_time timestamp NULL,
	query_time_roc text NULL,
	market text NOT NULL,
	product text NOT NULL,
	max_price numeric NULL,
	high_price numeric NULL,
	mid_price numeric NULL,
	low_price numeric NULL,
	avg_price numeric NULL,
	price_change_pct numeric NULL,
	volume int4 NULL,
	volume_change_pct numeric NULL,
	residual_volume int4 NULL,
	result_title text NULL,
	source_url text NULL,
	raw jsonb NULL,
	inserted_at timestamptz DEFAULT now() NOT NULL,
	updated_at timestamptz DEFAULT now() NOT NULL,
	CONSTRAINT flower_prod_day_trans_pkey PRIMARY KEY (trans_date, market, product)
);
-- +goose Up
-- +goose StatementBegin
CREATE TABLE public.flower_prod_day_trans (
	trans_date date NOT NULL,
	trans_date_roc text NULL,
	query_time timestamp NULL,
	query_time_roc text NULL,
	market text NOT NULL,
	product text NOT NULL,
	max_price numeric NULL,
	high_price numeric NULL,
	mid_price numeric NULL,
	low_price numeric NULL,
	avg_price numeric NULL,
	price_change_pct numeric NULL,
	volume int4 NULL,
	volume_change_pct numeric NULL,
	residual_volume int4 NULL,
	result_title text NULL,
	source_url text NULL,
	raw jsonb NULL,
	inserted_at timestamptz DEFAULT now() NOT NULL,
	updated_at timestamptz DEFAULT now() NOT NULL,
	CONSTRAINT flower_prod_day_trans_pkey PRIMARY KEY (trans_date, market, product)
);
-- +goose StatementEnd

-- +goose Down
-- +goose StatementBegin
SELECT 'down SQL query';
-- +goose StatementEnd

建立 down ( 因為如果是 要回復上步的話 就是把表刪除)

DROP TABLE IF EXISTS flower_prod_day_trans;

寫入後就會是

-- +goose Up
-- +goose StatementBegin
CREATE TABLE public.flower_prod_day_trans (
	trans_date date NOT NULL,
	trans_date_roc text NULL,
	query_time timestamp NULL,
	query_time_roc text NULL,
	market text NOT NULL,
	product text NOT NULL,
	max_price numeric NULL,
	high_price numeric NULL,
	mid_price numeric NULL,
	low_price numeric NULL,
	avg_price numeric NULL,
	price_change_pct numeric NULL,
	volume int4 NULL,
	volume_change_pct numeric NULL,
	residual_volume int4 NULL,
	result_title text NULL,
	source_url text NULL,
	raw jsonb NULL,
	inserted_at timestamptz DEFAULT now() NOT NULL,
	updated_at timestamptz DEFAULT now() NOT NULL,
	CONSTRAINT flower_prod_day_trans_pkey PRIMARY KEY (trans_date, market, product)
);
-- +goose StatementEnd

-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS flower_prod_day_trans;
-- +goose StatementEnd

執行

up

我是使用 postgrsql 且有使用 pgbouncer post 會是 6432

連線字串會是

postgres://{{account}}:{{passwrod}}@127.0.0.1:6432/postgres
goose  postgres "postgres://{{account}}:{{passwrod}}@127.0.0.1:6432/postgres" up

結果


2026/02/11 08:10:52 OK   20260211080725_init.sql (14.5ms)
2026/02/11 08:10:52 goose: successfully migrated database to version: 20260211080725

可以看到資料庫多一張表 goose_db_version

down

goose  postgres "postgres://{{account}}:{{passwrod}}@127.0.0.1:6432/postgres" down
2026/02/11 08:22:25 OK   20260211080725_init.sql (10.18ms)

就會看到表被刪除了

可以再 up嗎?

可以

可以觀察到 再次執行 up 後 goose_db_version 這張表 會看到

idversion_idis_appliedtstamp
10true2026-02-11 07:49:17.643
320260211080725true2026-02-11 08:23:28.866

有version_id號的資料

再次 down 的話 這條資料就會不見

模擬 如果是 需求 會一直迭代的話

新增 account 表

 goose create  account sql
CREATE TABLE public.account (
	account_id varchar(255) NOT NULL,
	created_date timestamp NOT NULL,
	last_modified_date timestamp NULL,
	created_by varchar(50) NOT NULL,
	last_modified_by varchar(50) NULL,
	first_name varchar(50) NULL,
	last_name varchar(50) NULL,
	"password" varchar(100) NULL,
	CONSTRAINT accountpk PRIMARY KEY (account_id),
);
vi 202602111630_account.sql
-- +goose Up
-- +goose StatementBegin
CREATE TABLE public.account (
	account_id varchar(255) NOT NULL,
	created_date timestamp NOT NULL,
	last_modified_date timestamp NULL,
	created_by varchar(50) NOT NULL,
	last_modified_by varchar(50) NULL,
	first_name varchar(50) NULL,
	last_name varchar(50) NULL,
	"password" varchar(100) NULL,
	CONSTRAINT accountpk PRIMARY KEY (account_id)
);
-- +goose StatementEnd

-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS account;
-- +goose StatementEnd

新增 country 表

 goose create  account scountryl
CREATE TABLE public.country (
	country varchar(255) NOT NULL,
	currency_symbol varchar(255) DEFAULT '$'::character varying NOT NULL,
	currency_code varchar(255) DEFAULT 'USD'::character varying NOT NULL,
	CONSTRAINT countrypk PRIMARY KEY (country)

);
vi 202602111630_country.sql
-- +goose Up
-- +goose StatementBegin
CREATE TABLE public.country (
	country varchar(255) NOT NULL,
	currency_symbol varchar(255) DEFAULT '$'::character varying NOT NULL,
	currency_code varchar(255) DEFAULT 'USD'::character varying NOT NULL,
	CONSTRAINT countrypk PRIMARY KEY (country)

);
-- +goose StatementEnd

-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS country;
-- +goose StatementEnd
goose  postgres "postgres://{{account}}:{{passwrod}}@127.0.0.1:6432/postgres" up

可以執行 看狀況

goose  postgres "postgres://{{account}}:{{passwrod}}@127.0.0.1:6432/postgres" status 

2026/02/11 08:40:19 Applied At Migration 2026/02/11 08:40:19 ======================================= 2026/02/11 08:40:19 Wed Feb 11 08:40:14 2026 — 20260211080725_init.sql 2026/02/11 08:40:19 Wed Feb 11 08:40:14 2026 — 20260211083030_account.sql 2026/02/11 08:40:19 Wed Feb 11 08:40:14 2026 — 20260211083301_country.sql

|id|version_id|is_applied|tstamp|
|--|----------|----------|------|
|1|0|true|2026-02-11 08:25:30.284|
|3|20260211080725|true|2026-02-11 08:33:38.961|
|4|20260211083030|true|2026-02-11 08:33:57.637|
|5|20260211083301|true|2026-02-11 08:34:28.999|

就會一次執行這新的兩張表

再次down

: goose postgres “postgres://{{account}}:{{passwrod}}@127.0.0.1:6432/postgres” down 2026/02/11 08:36:51 OK 20260211083301_country.sql (10.63ms) : goose postgres “postgres://{{account}}:{{passwrod}}@127.0.0.1:6432/postgres” down 2026/02/11 08:36:52 OK 20260211083030_account.sql (6.38ms) : goose postgres “postgres://{{account}}:{{passwrod}}@127.0.0.1:6432/postgres” down 2026/02/11 08:36:53 OK 20260211080725_init.sql (7.5ms)

他會逐次 慢慢往 更舊的版本退

單退掉某版 down-to

退到某個版本:

goose postgres “postgres://{{account}}:{{passwrod}}@127.0.0.1:6432/postgres” down-to 20260211080725

2026/02/11 08:42:25 Applied At Migration 2026/02/11 08:42:25 ======================================= 2026/02/11 08:42:25 Wed Feb 11 08:40:14 2026 — 20260211080725_init.sql 2026/02/11 08:42:25 Wed Feb 11 08:40:14 2026 — 20260211083030_account.sql 2026/02/11 08:42:25 Pending — 20260211083301_country.sql

清空所有 reset

goose postgres "postgres://{{account}}:{{passwrod}}@127.0.0.1:6432/postgres" reset

所有 透過 goose 管理的 表 就會乾乾淨淨

重新 洗表 最近一次的表 redo

goose postgres "postgres://{{account}}:{{passwrod}}@127.0.0.1:6432/postgres" redo

到某一個版本 up to

2026/02/11 08:49:12     Applied At                  Migration
2026/02/11 08:49:12     =======================================
2026/02/11 08:49:12     Wed Feb 11 08:46:58 2026 -- 20260211080725_init.sql
2026/02/11 08:49:12     Pending                  -- 20260211083030_account.sql
2026/02/11 08:49:12     Pending                  -- 20260211083301_country.sql

goose postgres "postgres://{{account}}:{{passwrod}}@127.0.0.1:6432/postgres" up-to 20260211083301

執行完後

再執行

goose postgres "postgres://{{account}}:{{passwrod}}@127.0.0.1:6432/postgres" status
2026/02/11 08:49:32     Applied At                  Migration
2026/02/11 08:49:32     =======================================
2026/02/11 08:49:32     Wed Feb 11 08:46:58 2026 -- 20260211080725_init.sql
2026/02/11 08:49:32     Wed Feb 11 08:49:29 2026 -- 20260211083030_account.sql
2026/02/11 08:49:32     Wed Feb 11 08:49:29 2026 -- 20260211083301_country.sql

他就直接跳版本了

下次就會了