在 PostgreSQL 的 Transaction 中即時讀取剛剛 INSERT 的資料:設計與實務經驗分享

| SQL | 2 Reads

在日常開發中,我們經常會遇到這樣的需求流程:

  1. 新增一筆資料(INSERT)到資料表。

  2. 緊接著查詢剛剛新增的這筆資料的欄位(SELECT)。

  3. 根據這筆查詢結果,執行後續的資料更新(UPDATE)。

那麼問題來了:在同一個交易(Transaction)中,我們真的能馬上 SELECT 到剛剛 INSERT 的資料嗎?

答案是——當然可以!
以下是我們在實務專案中遇到的實際情境與 PostgreSQL 的設計解釋。


一個實際範例

我們在一個 foreach (groups) 迴圈中執行以下流程:

-- Step 1: 新增資料
INSERT INTO link_tbl (link_cd, kobetu_cd, matome_cd_uke, ...)
VALUES (...);

-- Step 2: 查詢剛剛那筆的 matome_cd_uke
SELECT matome_cd_uke
FROM link_tbl
WHERE kobetu_cd = :kc
ORDER BY link_cd DESC
LIMIT 1;

這兩段 SQL 都是在 同一個 NpgsqlConnection同一個 NpgsqlTransaction tran 中執行的。

結果就是:我們的 SELECT 查詢可以即時讀到剛剛 INSERT 的資料,毫無問題。


PostgreSQL 的保證:Read Committed 隔離層級

PostgreSQL(以及大多數主流的 RDBMS,例如 SQL Server、MySQL)預設使用的交易隔離層級是 Read Committed

這代表:

  • 在同一個交易(Transaction)中,你自己所做的 INSERT、UPDATE,立刻就能被後續的 SELECT 查到。

  • 只有其他連線(Connection)上的查詢,必須等到你 COMMIT 之後,才會看到這筆資料。

所以,只要你確保以下兩點:

  • SELECT、INSERT、UPDATE 都使用 同一條 Connection

  • 並且共用 同一個 Transaction tran

那麼就可以放心地在程式執行順序上,先 INSERT → 後 SELECT,完全查得到。


實務上的保險作法

雖然我們知道 SELECT 查得到剛剛 INSERT 的資料,但我們也會這麼寫:

SELECT matome_cd_uke
FROM link_tbl
WHERE kobetu_cd = :kc
ORDER BY link_cd DESC
LIMIT 1;

這裡的 ORDER BY link_cd DESC LIMIT 1 是保險機制:
假設歷史上有多筆同一個 kobetu_cd 的資料,我們一定要拿「最新那筆」,這通常由自增欄位 link_cd 控制。


Bonus:減少資料庫查詢次數的技巧(版本 B)

如果你希望減少資料庫的查詢次數(尤其在大量迴圈中),還可以考慮這個優化:

  • 第一次 INSERT 完之後,直接將 (kobetu_cd, matome_cd_uke) 存入一個 Dictionary

  • 後續要用時,直接從 Dictionary 取用,而不是再次 hit 資料庫

兩種做法都正確,看你是否偏向資料庫即時查詢,還是 prefer 將中間結果緩存起來、減少 DB 負擔。


結語

這篇文章的重點總結如下:

✅ PostgreSQL 在同一個交易中能馬上 SELECT 到自己剛剛 INSERT 的資料
✅ Read Committed 是 PostgreSQL 預設的隔離層級,提供這項保證
ORDER BY DESC LIMIT 1 是實務中常用的查詢技巧,確保抓到最新一筆
✅ 如需效能最佳化,可將結果暫存在 Dictionary,避免重複查詢

透過掌握這些交易與隔離層級的概念,你就能更靈活且高效地設計資料處理流程。

This article was last edited at