掌握多層嵌套 JSONB 的查詢技巧:從入門到進階
更新日期: 2025 年 3 月 25 日
在 PostgreSQL 的資料世界裡,JSONB
是個非常靈活而強大的資料類型。
它能夠儲存結構化卻不固定格式的資料,因此廣泛被用在 API response、NoSQL 式的資料欄位、甚至是活動紀錄等場景。
但這份自由也有代價:當資料開始出現巢狀結構,甚至巢中有陣列、陣列中又有物件時,查詢寫起來往往讓人懷疑人生。
這篇文章會從最基礎的概念出發,一步一步帶你掌握查詢多層嵌套 JSONB 的核心技巧,不只學語法,更建立你的「資料結構處理思維」。
JSONB 基礎複習:你真的理解 JSONB 嗎?
在進入查詢技巧之前,我們先回到最基本的問題——什麼是 JSON?什麼又是 JSONB?它們有什麼不同?
JSON vs JSONB:看似相同,實則大不同
PostgreSQL 提供兩種格式來儲存 JSON 資料:JSON
與 JSONB
。
雖然兩者都可以儲存結構化的資料,但底層運作方式大不相同,這也會影響你的查詢效率與操作彈性。
類型 | 說明 |
---|---|
JSON | 以純文字(text)格式儲存資料。資料會以原始樣貌保留,包括欄位順序與空格,優點是符合 JSON 標準,但每次查詢時都需要即時解析資料,效能較差。 |
JSONB | Binary(位元)格式儲存資料。會先解析 JSON 結構再儲存,因此欄位順序會被移除,但換來的是更快的查詢速度,並且支援索引操作。非常適合在資料表中經常需要查詢、篩選的場景。 |
🔍 什麼時候該用哪一個?
- 如果你只想「原樣儲存 JSON 資料,不查它」,可以用
JSON
- 如果你想「查詢 JSON 裡的欄位、值,甚至做條件篩選」,就用
JSONB
✅ 簡單一句話: JSON 是原始資料格式,JSONB 是為了查資料而生的進化版本。
巢狀結構為何常見?
如果你之前有處理過前端 API 或 NoSQL 文件資料格式,就會發現 JSON 資料幾乎都是巢狀結構。
這並不是故意為難資料庫設計師,而是因為這樣的結構更貼近「真實世界的資料邏輯」。
來看看一個模擬的 JSON 資料,描述一個使用者與他發表的文章:
{
"user": {
"id": 1,
"name": "Alice",
"posts": [
{"title": "第一篇", "likes": 10},
{"title": "第二篇", "likes": 5}
]
}
}
這種資料設計為什麼實用?讓我們一層一層拆解來看:
結構層級 | 描述 |
---|---|
user | 是一個物件(Object),代表一位使用者 |
user.id 和 user.name | 是使用者的基本資料 |
user.posts | 是一個陣列,裡面放著使用者發表的所有貼文 |
posts[0]、posts[1] | 每篇貼文又是獨立的物件,內含 title 與 likes 欄位 |
這樣的巢狀結構有幾個好處:
- 直觀且符合邏輯:貼文是屬於使用者的,所以放在
user
裡很自然。 - 彈性高:可以放任意多的貼文,不需要修改資料表 schema。
- 節省關聯查詢:不需要 JOIN 多個表格就能一次取得完整資訊。
巢狀結構會有哪些型態?
巢狀不只一種,有幾種常見的搭配方式:
- 物件中嵌套物件(Object in Object)
- 物件中嵌套陣列(Array in Object)
- 陣列中又是物件(Object in Array)
- 陣列中嵌套陣列(Array in Array,較少見)
用圖解的話,大概長這樣:
{
A: {
B: [
{ C: 123 },
{ C: 456 }
]
}
}
- A 是外層物件
- B 是物件內的陣列
- 陣列裡每個元素又是物件
巢狀結構的代價是什麼?
當然,好處多,不代表沒有代價。巢狀資料結構的缺點就是——查詢起來麻煩,語法也容易變長且複雜。
而這正是這篇文章接下來要解決的問題:如何用最清晰的方式,查詢這些巢狀 JSONB 結構?
所以別擔心巢太深,只要你跟著學會正確的操作方式,處理這種資料就像剝洋蔥一樣,一層一層來,清楚又有條理!
基本操作語法:->
與 ->>
怎麼用?
在 PostgreSQL 操作 JSONB
資料時,最基本、最常見的兩個運算子就是:
->
:取出 JSON 格式的值,結果仍保留為 JSON->>
:取出 文字格式的值,結果為純文字(text)
這兩個符號的差異是初學者的第一個關卡,但一旦理解,你就能靈活存取任何一層的資料。
使用 ->
:取出 JSON 格式的欄位值(仍為 JSON)
當你使用 ->
時,PostgreSQL 會回傳資料中指定欄位的值,但保留原本的 JSON 格式,也就是說——你拿到的仍然是一段 JSON,而不是單純的文字。
這個語法特別適合,在你還需要繼續往下存取巢狀欄位,或者打算用其他 JSON 函數處理時使用。
🧪 查詢範例
假設我們有一筆使用者資料如下,儲存在 users
資料表的 data
欄位中:
{
"user": {
"id": 1,
"name": "Alice",
"posts": [
{ "title": "第一篇", "likes": 10 },
{ "title": "第二篇", "likes": 5 }
]
}
}
若你想取出整個 user
區塊,可以這樣寫:
SELECT data->'user' AS user_object
FROM users;
📌 查詢結果會是:
{
"id": 1,
"name": "Alice",
"posts": [
{ "title": "第一篇", "likes": 10 },
{ "title": "第二篇", "likes": 5 }
]
}
這段結果仍然是 JSON 格式,你可以用來:
- 再往下存取某個欄位(例如
->'name'
) - 交給其他 JSON 處理函式(如
jsonb_array_elements()
) - 組裝成新的 JSON 結構
❗️限制:不能直接比較或排序
因為結果是 JSON 而非文字,下面這種寫法會報錯:
-- 錯誤!不能對 JSON 直接做排序
SELECT * FROM users
ORDER BY data->'user'->'name';
你會看到錯誤訊息類似:
ERROR: could not identify an ordering operator for type jsonb
所以,如果你想進行比較、排序或文字操作,就得改用 ->>
。
使用 ->>
:取出文字格式的欄位值(轉成 text)
當你使用 ->>
時,PostgreSQL 會幫你把目標欄位的值取出後,轉成文字格式(text),這樣就可以進行字串比對、排序或套用文字函數。
這是你在日常查詢中最常用的格式,特別是當你需要進行條件篩選或展示資料時。
🧪 查詢範例
假設你只想取得使用者名稱(name
欄位的值),可以這樣寫:
SELECT data->'user'->>'name' AS username
FROM users;
📌 查詢結果:
username |
---|
Alice |
這裡的 data->'user'->>'name'
意思是:
- 先從
data
中取出user
這個欄位(為 JSON 格式) - 接著從
user
裡面取出name
欄位,並用->>
將它轉為文字格式
✅ 文字格式的好處
使用 ->>
取出的資料是文字,你可以:
1️⃣ 加上 WHERE
條件
SELECT *
FROM users
WHERE data->'user'->>'name' = 'Alice';
2️⃣ 做排序
SELECT data->'user'->>'name' AS username
FROM users
ORDER BY username ASC;
3️⃣ 使用文字函式處理
SELECT
data->'user'->>'name' AS username,
LENGTH(data->'user'->>'name') AS name_length,
LOWER(data->'user'->>'name') AS lowercase_name
FROM users;
常見誤解:我可不可以只用一種就好?
這是很多初學者的疑問,但答案是——不行,因為兩者用途不同:
情境 | 應該使用 |
---|---|
還要繼續往下一層取值(巢狀結構) | -> |
要比對值、排序、當作文字處理 | ->> |
要把資料餵給 jsonb_array_elements() 等函式 | -> |
正確地交替使用 ->
和 ->>
,才是查詢 JSONB 的關鍵技巧。
🧩 小提醒:兩者可以混搭使用
你可以在查詢中同時使用 ->
和 ->>
,例如:
SELECT
data->'user'->>'name' AS username,
data->'user'->'posts' AS user_posts
FROM users;
這會同時取出:
- 使用者名稱(文字格式)
- 使用者貼文列表(JSON 陣列)
非常適合在需要顯示資訊 + 保留原始結構時使用。
🧠 小技巧:你其實在用「鏈結存取」
當你寫出這樣的語法時:
data->'user'->'posts'->0->>'title'
你其實在做「逐層存取」:
data->'user'
:取得使用者物件->'posts'
:取得貼文陣列->0
:取得陣列中的第一篇貼文(索引從 0 開始)->>'title'
:從該貼文中取出title
欄位,並轉為文字
這樣的寫法其實是「一路往下挖」,類似你在 JavaScript 中這樣取值:
data.user.posts[0].title
⚠️ 為什麼這種寫法不夠好?
雖然這樣寫可以正常執行,但它有幾個缺點:
- 可讀性差:看不出哪一層是物件、哪一層是陣列
- 維護困難:資料結構改變時容易錯位
- 無法應對多筆資料:只能拿到「一筆」(例如 posts[0]),但現實情況你很可能想查出「所有貼文」
因此,如果你想要處理的是陣列(像是所有貼文),就該學會更標準的方式來展開這些資料,這正是我們下一章會介紹的重點——jsonb_array_elements()
。
認識 ->0
:巢狀結構裡的第一堂課
在處理 JSONB 巢狀資料時,你很快就會遇到一個語法:->0
。
這個語法的意思是:從 JSON 陣列中,取出第 0 筆(也就是第一筆)資料。
就像 JavaScript 裡的 array[0]
一樣,索引從 0 開始。
不過,這個語法有個關鍵前提:只有「陣列」才可以使用索引 ->0
。如果你不小心對一個「物件」使用 ->0
,就會報錯。
什麼是陣列?什麼是物件?
我們來看一段實際的 JSON 資料結構(儲存在 users
資料表的 data
欄位中):
{
"user": {
"id": 1,
"name": "Alice",
"posts": [
{ "title": "第一篇", "likes": 10 },
{ "title": "第二篇", "likes": 5 }
]
}
}
結構說明:
欄位路徑 | 類型 | 說明 |
---|---|---|
data | Object | 最外層物件 |
data->'user' | Object | 使用者資訊 |
data->'user'->'posts' | Array | 貼文陣列,每篇貼文是物件 |
✅ 正確使用 ->0
的範例:取出陣列中的第一筆資料
因為 posts
是一個陣列,所以你可以使用索引來存取裡面的貼文。例如,取出第一篇貼文的標題:
SELECT data->'user'->'posts'->0->>'title' AS first_post_title
FROM users;
操作順序解析:
data->'user'
:取得使用者物件(Object)->'posts'
:進入posts
陣列->0
:取得陣列中的第一筆資料(第一篇貼文)->>'title'
:從該貼文物件中取出標題,並轉為文字格式
📌 查詢結果範例如下:
first_post_title |
---|
第一篇 |
❌ 常見錯誤:「我以為 ->0
可以用在任何東西」
你可能會想寫:
SELECT data->'user'->0->>'name' AS name
FROM users;
這會報錯,因為 data->'user'
是一個 物件(object),你不能對物件使用索引 ->0
。只有陣列才可以這樣取值。
PostgreSQL 會回傳類似以下錯誤訊息:
ERROR: cannot extract array element from a non-array
判斷型別後決定用法
簡單整理如下:
資料類型 | 正確存取方式 |
---|---|
Object(物件) | 使用 key 名稱:->'key' |
Array(陣列) | 使用索引值:->0、->1 |
你可以透過 jsonb_typeof()
函式來確認一段 JSON 是不是陣列:
SELECT jsonb_typeof(data->'user'->'posts') AS type
FROM users;
-- 結果會是:array
⚠️ 限制:->0
只能取單筆,無法查多筆或做統計
雖然 ->0
很直觀,能快速抓第一筆資料,但它的用途其實非常有限。舉例來說:
- 你只能取得一筆貼文
- 你沒辦法用它查出所有貼文
- 你也無法對貼文做加總、排序、篩選等操作
為什麼我們需要展開陣列?
當你面對一個像這樣的貼文陣列:
"posts": [
{ "title": "第一篇", "likes": 10 },
{ "title": "第二篇", "likes": 5 }
]
你可能會想:
- 我要統計每篇貼文的按讚數
- 我要列出所有貼文標題
- 我要找出按讚最多的那一篇
這時候,你就不能只靠 ->0
。因為:
❗️ ->0
是「定點取值」
只能拿第一篇,沒有辦法變出 2、3、4… 或 10 篇資料。
如果你有一位使用者有 10 篇貼文,你不會想手動寫 ->0
到 ->9
吧?這時候,就必須用 PostgreSQL 提供的強大函式 —— jsonb_array_elements()
,來展開整個貼文陣列。
這也正是我們下一節要介紹的重點。
✅ jsonb_array_elements()
是「展開整包」
它能把整個陣列展開,每個元素都變成一筆獨立資料列。
這樣你才能對「每篇貼文」進行分析,而不只是看到第一篇。
這就是處理巢狀陣列結構的真正起點:你不只是讀資料,而是要讓資料「攤平」才能好好查詢。
jsonb_array_elements()
:從陣列變資料列的關鍵轉換
它的用途是什麼?
當你有一個 JSON 陣列,像這樣:
"posts": [
{ "title": "第一篇", "likes": 10 },
{ "title": "第二篇", "likes": 5 }
]
你不能單靠 ->0
、->1
去一筆一筆查,太不實用。
這時候 PostgreSQL 提供的函式 jsonb_array_elements()
就派上用場了。
這個函式可以:
- 將 一個 JSON 陣列 拆解成 多筆資料列
- 搭配
LATERAL
使用,可處理巢狀資料 - 讓你可以對每筆陣列元素做個別查詢、計算、統計
語法解析
jsonb_array_elements(jsonb_value) AS alias
這段語法是 PostgreSQL 處理 JSON 陣列時非常關鍵的一步,它的用途可以用一句話來概括:
把一個陣列拆成多筆資料列(rows),讓你能對每個元素單獨操作。
我們一層一層來解釋這句語法到底做了什麼。
元件 | 說明 |
---|---|
jsonb_array_elements() | PostgreSQL 提供的函式,用來展開 JSON 陣列 |
jsonb_value | 一段 JSONB 格式的資料,這必須是一個陣列,否則會報錯 |
AS alias | 幫展開出來的每一筆元素命名一個暫時的別名,你後面才能寫 alias->>'欄位' 來存取每個元素的內容 |
功能說明:從「一欄一筆」變成「一欄多筆」
假設你原本的 data
欄位長這樣:
{
"user": {
"name": "Alice",
"posts": [
{ "title": "第一篇", "likes": 10 },
{ "title": "第二篇", "likes": 5 }
]
}
}
你想把 posts
裡面那兩篇文章「個別列出來」,不想它們卡在同一個陣列裡。
這時你就可以這樣寫:
FROM users,
LATERAL jsonb_array_elements(data->'user'->'posts') AS post
這段語法會:
- 進入
data->'user'->'posts'
陣列 - 用
jsonb_array_elements()
把這個陣列展開 - 每一個貼文物件變成一筆獨立資料列
- 並用
AS post
指定每一筆展開出來的資料命名為post
🔍 展開前 vs 展開後
展開前(原始 JSON 陣列):
id | data |
---|---|
1 | {"user": {"name": "Alice", "posts": [ {...}, {...} ] }} |
展開後(每筆貼文一行):
username | post |
---|---|
Alice | { "title": "第一篇", "likes": 10 } |
Alice | { "title": "第二篇", "likes": 5 } |
這樣一來,你就可以在 SELECT
裡用 post->>'title'
、post->>'likes'
等語法,像查普通資料表欄位一樣,操作每一筆貼文資料。
💬 為什麼這叫「打平」?
所謂的「打平」意思是:原本是一欄裡面放一整包陣列資料,經過 jsonb_array_elements()
後,這包資料被「攤開」,每個元素獨立變成一列。
- 想像你有一個紙箱(這是
posts
陣列) - 裡面裝了很多張寫有筆記的小紙條(每一張是貼文物件)
- 在箱子沒打開前,從外面看就是一個箱子(只佔一筆欄位)
- 當你把紙箱打開,把每張紙條一張張攤開來排在桌上
- 你就能一張張看、一張張分析、甚至一張張丟掉、加起來!
這個「打開紙箱 → 攤開內容物」的動作,就是資料庫裡的「打平」。
接下來我們會進一步說明 LATERAL
的角色,幫你破解它看似神秘、其實超實用的本質!
LATERAL 是什麼?為什麼少了它查詢會錯?
🎯 先說結論:
當你在 FROM 中使用一個需要依賴其他欄位值的函式時,LATERAL 可以讓它正確存取每一列的資料。
🧩 我們先來看一個你很熟的查詢語法:
SELECT
data->'user'->>'name' AS username,
post->>'title' AS post_title
FROM users,
jsonb_array_elements(data->'user'->'posts') AS post;
這段的目的是什麼?你希望:
- 先從
users
表中抓出每一筆data
- 再從每一筆資料裡的
posts
陣列中,把所有貼文「展開」 - 每篇貼文變成一筆資料列,讓你可以查標題和按讚數
聽起來很合理,語法也沒錯太多,但你忘了一件事:PostgreSQL 不知道「你現在要抓的是哪一列的 data
」!
😵 PostgreSQL 心裡的困惑
當 PostgreSQL 執行這段查詢時,它其實是這樣想的:
好,我知道你叫我展開
data->'user'->'posts'
裡的貼文,但你沒告訴我是哪一筆
users
的資料,我根本不知道該抓誰的data
啊!
✅ 加上 LATERAL
:就像把展開函式「貼在每一筆資料旁邊」
當你這樣改寫:
FROM users,
LATERAL jsonb_array_elements(data->'user'->'posts') AS post
SQL 執行順序就變了!它會這樣理解:
喔~你是要我「對每一列
users
資料」跑一次jsonb_array_elements(...)
,而且是用這一列的data
來展開posts
陣列。我懂了!那我就每列跑一次、展開一次,然後幫你一筆筆貼文列出來。
這就是 LATERAL
的角色。它就像是個「讓函式看得見當前資料列」的標記。
📦 更直觀的比喻:逐列開盒子
想像 users
表長這樣:
id | data(裡面有每個使用者的 posts 陣列) |
---|---|
1 | 一個有 2 篇貼文的使用者 |
2 | 一個有 3 篇貼文的使用者 |
現在你想針對每一位使用者,打開他的貼文盒子,把每篇貼文展開列出來。
如果你沒有加 LATERAL,資料庫就像是:
「你跟我說你要展開一個盒子,但你沒有告訴我是哪一位使用者的盒子啊?」
加了 LATERAL
,資料庫就知道:
「喔,每一筆資料我都去展開它自己的貼文,沒問題!」
❌ 不加 LATERAL:報錯範例
SELECT ...
FROM users,
jsonb_array_elements(data->'user'->'posts') AS post;
錯誤訊息可能會長這樣:
ERROR: invalid reference to FROM-clause entry for table "users"
DETAIL: There is an entry for table "users", but it cannot be referenced from this part of the query.
這就是 PostgreSQL 告訴你:「你在 FROM 裡寫的函式,不能看前面那個 users
表的欄位。」
為什麼 SQL 要這麼設計?
要真正理解 LATERAL
為什麼必要,我們要先了解 SQL 的執行順序,特別是 FROM
子句的處理邏輯。
🔄 SQL 執行查詢的順序
雖然我們寫查詢的順序通常是:
SELECT ... FROM ... WHERE ... ORDER BY ...
但實際上,SQL 執行時是這樣處理的:
- FROM:先確定要從哪裡取資料(資料表或子查詢)
- JOIN / LATERAL:處理來源之間的關聯
- WHERE:套用條件
- SELECT:決定要顯示哪些欄位
- (後面還有 GROUP BY、ORDER BY 等)
也就是說,在執行 SELECT
之前,SQL 會先處理 FROM
子句裡的所有來源資料。
📌 那問題來了:如果你在 FROM
裡面,就需要了一個「還正在確認中的欄位」的函式呢?
像這樣的查詢:
FROM users,
jsonb_array_elements(data->'user'->'posts') AS post
這裡 jsonb_array_elements()
這個函式想要用的是來自 users
資料表中的 data
欄位。
但根據剛剛提到的執行順序,SQL 在處理 FROM
的每個項目時,是彼此獨立的。
在這個階段,users
和 jsonb_array_elements(...)
是平行的兩個來源,函式是沒辦法自動「回頭」去看 users
的欄位的。
❗️這就違反了 SQL 的邏輯規則:
- 你說「我想展開每一列使用者的
posts
陣列」 - 但 SQL 回你:「我現在還沒讀完 users,
data
是什麼我不知道,你怎麼能叫我用?」
這就像廚師在備料時,說要準備「這道菜需要的配料」,但你沒給他哪一道菜的名字。
他會問:「我要準備哪一道菜的料?我不知道你指的是哪一道啊!」
加上
LATERAL
,就是你告訴他:「這是麻婆豆腐的配料清單,依這道來準備。」
✅ 解法就是:加上 LATERAL
當你這樣寫:
FROM users,
LATERAL jsonb_array_elements(data->'user'->'posts') AS post
你就是明確地告訴 SQL:
「這個函式不是獨立執行的,它要依附在每一列
users
的資料上,請幫我一列一列跑,讓它每次都可以讀到當前這列的data
值。」
🧠 為什麼 PostgreSQL 不自動幫你加?
因為這會讓查詢變得不明確、不穩定、難維護。
SQL 設計者希望開發者能主動聲明依賴關係,這樣資料庫才能:
- 清楚知道哪些函式會依賴哪些資料
- 在執行計畫中做更好的優化
- 避免語意不清或潛在的模糊行為
✅ 小結:從執行順序理解 LATERAL 的必要性
概念 | 解釋 |
---|---|
SQL 先執行 FROM | 所以 FROM 裡的每個項目要彼此獨立執行 |
函式想存取上一個資料表的欄位 | 這就打破了平行原則,SQL 不知道你想用哪一列 |
LATERAL 的作用 | 告訴 SQL:這個函式是「要針對每一筆資料列來執行的」 |
實戰範例:展開所有貼文,取得每篇的標題與按讚數
🎯 目標:列出每位使用者的所有貼文標題與按讚數
假設我們現在有一張名為 users
的資料表,每筆資料中有一個 data
欄位,裡面是 JSONB 結構如下:
{
"user": {
"name": "Alice",
"posts": [
{ "title": "第一篇", "likes": 10 },
{ "title": "第二篇", "likes": 5 }
]
}
}
這裡的 posts
是一個陣列,裡面包含多篇貼文。我們的目標是把這個陣列展開,讓每一篇貼文都變成一筆資料列,並且能取得:
- 使用者名稱
- 每一篇貼文的標題
- 每一篇貼文的按讚數
✅ 查詢語法
SELECT
data->'user'->>'name' AS username,
post->>'title' AS post_title,
post->>'likes' AS like_count
FROM users,
LATERAL jsonb_array_elements(data->'user'->'posts') AS post;
查詢流程逐步解析
步驟 | 語法片段 | 說明 |
---|---|---|
① | data->'user'->'posts' | 從 data 欄位中取出 user 物件,接著進入 posts 陣列。這邊的資料格式仍然是 JSONB 陣列(array),尚未展開。 |
② | jsonb_array_elements(...) | 將剛剛取出的陣列展開,一個元素變成一筆資料列,就是俗稱的「打平」。 |
③ | AS post | 幫展開出來的每個元素指定一個別名叫做 post,之後就可以用 post->>'欄位' 來讀取它裡面的值。 |
④ | LATERAL | 這是關鍵字,讓 jsonb_array_elements() 可以存取同一筆 users 資料中的其他欄位(這裡是 data)。少了它,查詢會失敗。 |
⑤ | post->>'title'、post->>'likes' | 從展開後的每筆貼文資料中,取出標題與按讚數,並轉為文字格式。 |
📌 查詢結果會長這樣:
username | post_title | like_count |
---|---|---|
Alice | 第一篇 | 10 |
Alice | 第二篇 | 5 |
每一筆資料都是一篇貼文,現在你就可以像處理一般表格資料一樣,對這些貼文做篩選、排序、計算等操作。
展開後的資料可以做什麼?
展開陣列後,你可以針對每篇貼文進行:
1️⃣ 排序
找出讚數最多的貼文:
ORDER BY (post->>'likes')::int DESC
2️⃣ 篩選
只查出按讚數超過 5 的貼文:
WHERE (post->>'likes')::int > 5
3️⃣ 統計
想算出使用者的總讚數、平均讚數,就可以把 post->>'likes'
轉為整數後用聚合函數操作,例如:
SELECT
data->'user'->>'name' AS username,
SUM((post->>'likes')::int) AS total_likes,
AVG((post->>'likes')::int) AS avg_likes
FROM users,
LATERAL jsonb_array_elements(data->'user'->'posts') AS post
GROUP BY username;
🔁 延伸應用:加入條件篩選
以下範例:只列出那些「每篇貼文讚數大於 5」的資料:
SELECT
data->'user'->>'name' AS username,
post->>'title' AS post_title
FROM users,
LATERAL jsonb_array_elements(data->'user'->'posts') AS post
WHERE (post->>'likes')::int > 5;
這樣查詢就能排除那些比較冷門的貼文。
使用 jsonb_array_elements()
搭配 LATERAL
是進入 JSONB 實戰應用的核心組合,能讓你從「看得到資料」走向「查得出來資料」,這正是資料查詢邏輯的進階一步。
結語:熟悉語法,駕馭巢狀資料不再是夢
處理巢狀 JSONB 結構,確實需要多一點思考與耐心。但一旦你掌握了這幾個核心工具:
->
與->>
的基本操作jsonb_array_elements()
拆解陣列LATERAL
正確使用方法
你就能駕馭幾乎任何形式的 JSON 資料結構。多試幾次、寫幾個查詢,從挫折中成長,很快你就會像老手一樣自在地操作巢狀資料。