從 SQL 資料庫中獲取多表相關資料可能會非常昂貴。Prisma ORM 現在允許您在資料庫層面和應用層面連線之間進行選擇,以便您為關係查詢選擇最高效的方法。
目錄
Prisma ORM 新功能:選擇最佳連線策略 🎉
對資料庫層面連線的支援是 Prisma ORM 中最受歡迎的功能之一,我們很高興地宣佈,它現在作為另一種查詢策略提供!
對於任何帶有 include(或 select)的關係查詢,現在在頂層有一個名為 relationLoadStrategy 的新選項。此選項接受以下兩個可能值之一:
join(預設):使用資料庫層面連線策略在資料庫中合併資料。query:透過向單個表傳送多個查詢,並在應用層合併資料來使用應用層面連線策略。
要啟用新的 relationLoadStrategy,您首先需要將預覽功能標誌新增到 Prisma Client 的 generator 塊中
注意:
relationLoadStrategy僅適用於 PostgreSQL 和 MySQL 資料庫。
完成此操作後,您需要重新執行 prisma generate 以使此更改生效,並在查詢中選擇關係載入策略。
這是一個使用新 join 策略的示例
請注意,由於 "join" 是預設值,relationLoadStrategy 選項在上面的程式碼片段中理論上也可以省略。我們在此處顯示它僅用於說明目的。
join 與 query — 何時使用?
現在有了這兩種查詢策略,您可能會想:何時使用哪種?
由於 Prisma ORM 在 PostgreSQL 上使用的橫向、聚合 JOIN 以及在 MySQL 上使用的關聯子查詢,join 策略在大多數情況下可能更高效(後續部分將詳細介紹)。資料庫引擎非常強大,擅長最佳化查詢計劃。這種新的關係載入策略正是為了充分利用這一點。
但是,在某些情況下,您可能仍然希望使用 query 策略,即為每個表執行一個查詢並在應用層面合併資料。根據資料集和模式中配置的索引,傳送多個查詢可能會更高效。對您的查詢進行效能分析和基準測試對於識別這些情況至關重要。
另一個考慮因素是複雜連線查詢所帶來的資料庫負載。如果由於某種原因資料庫伺服器上的資源稀缺,您可能希望將複雜連線查詢(包含過濾和分頁)所需的繁重計算轉移到您的應用伺服器,因為應用伺服器可能更容易擴充套件。
總結
- 在大多數情況下,新的
join策略會更高效。 - 在某些邊緣情況下,
query可能會更高效,具體取決於資料集和查詢的特性。我們建議您對資料庫查詢進行效能分析以識別這些情況。 - 如果您想節省資料庫伺服器上的資源,並在可能更容易擴充套件的應用伺服器中執行資料合併和轉換的繁重工作,請使用
query。
理解SQL資料庫中的關係
現在我們已經瞭解了 Prisma ORM 的 JOIN 策略,接下來讓我們回顧一下 SQL 資料庫中關係查詢通常是如何工作的。
關係的扁平與巢狀資料結構
SQL 資料庫以扁平(即規範化)方式儲存資料。實體之間的關係透過外部索引鍵表示,這些外部索引鍵指定了跨表的引用。
另一方面,應用開發人員通常習慣於處理巢狀資料,即可以任意深度巢狀其他物件的物件。
這是一個巨大的差異,不僅在於資料在磁碟和記憶體中的物理佈局方式,還在於資料的心智模型和推理方式。
關係資料需要為應用開發者進行“合併”
由於相關資料在資料庫中是物理上分開儲存的,因此需要將其在某個地方合併,以成為應用開發人員熟悉的巢狀結構。這種合併也稱為“連線(join)”。
這種連線可以在兩個地方發生:
- 在資料庫層面:向資料庫傳送單個 SQL 查詢。該查詢使用
JOIN關鍵字或關聯子查詢,讓資料庫在多個表之間執行連線,並返回巢狀結構。 - 在應用層面:向資料庫傳送多個查詢。每個查詢只訪問一個表,然後查詢結果在應用層進行記憶體合併。這曾是
v5.9.0之前 Prisma Client 唯一支援的查詢策略。
哪種方法更可取取決於所使用的資料庫、資料集的大小和特性以及查詢的複雜性。請繼續閱讀,瞭解何時推薦使用哪種策略。
底層原理是什麼?
Prisma ORM 透過在 PostgreSQL 中使用 LATERAL 連線和資料庫層面的 JSON 聚合(例如透過 json_agg),以及在 MySQL 中使用關聯子查詢,實現了新的 join 關係載入策略。
在以下部分中,我們將探討為什麼 PostgreSQL 上的 LATERAL 連線和資料庫層面的 JSON 聚合方法比簡單、傳統的 JOIN 更高效。
使用 JSON 聚合防止查詢結果中的冗餘
在使用資料庫層面的 JOIN 時,有多種構建 SQL 查詢的選項。讓我們考慮上面 Prisma schema 的 SQL 表定義
要檢索所有使用者及其帖子,您可以使用簡單的 LEFT JOIN 查詢
這是使用一些示例資料時可能的結果:

請注意本例中 user_name 列的冗餘。連線的表越多,這種冗餘就會越嚴重。例如,假設有另一個 Comment 表,其中每個評論都有一個指向 Post 表中記錄的 postId 外部索引鍵。
這是一個表示該情況的 SQL 查詢
現在,假設第一篇帖子有多個評論

在這種情況下,結果集的大小隨著連線表數量的增加呈指數級增長。由於這些資料需要透過網路從資料庫傳輸到應用伺服器,這可能會變得非常昂貴。
Prisma 實現的在資料庫層面使用 JSON 聚合的 join 策略解決了這個問題。
這是一個 PostgreSQL 的示例,它使用 json_agg 和 json_build_object 來解決冗餘問題,並以 JSON 格式返回每個使用者的帖子。
這次的結果集不包含冗餘資料。此外,資料結構已經方便地具備了 Prisma Client 返回的形狀,這節省了查詢引擎中轉換結果的額外工作。

使用橫向 JOIN 實現更高效的帶分頁和過濾的查詢
關係查詢(像大多數其他查詢一樣)幾乎從不獲取表中的所有資料,而是帶有額外的結果集約束,如過濾和分頁。特別是分頁在使用傳統 JOIN 時會變得非常複雜,讓我們看另一個示例。
考慮這個 Prisma Client 查詢,它獲取 10 個使用者以及每個使用者的 5 篇帖子
當以原始 SQL 編寫時,您可能會想在子查詢中使用 LIMIT 子句,例如:
但是,這不起作用,因為內部的 SELECT 實際上並未返回每個使用者五篇帖子 — 相反,它總共返回了兩篇帖子,這顯然不是我們想要的結果。
使用傳統的 JOIN,可以透過使用 row_number() 函式為結果集中的記錄分配遞增的整數來解決,從而可以手動執行分頁計算。
然而,這種方法會很快變得非常複雜,因此不適合構建分頁關係查詢。
維護、擴充套件和除錯這類 SQL 查詢令人望而生畏,可能耗費數小時的開發時間。
值得慶幸的是,較新的資料庫版本透過一種新型查詢解決了這個問題:橫向 JOIN。
上述查詢可以透過使用 LATERAL 關鍵字進行簡化
這不僅使查詢更具可讀性,而且資料庫引擎也可能更能夠最佳化查詢,因為它能更好地理解查詢的意圖。
結論
讓我們回顧一下使用 Prisma 連線關係查詢資料時的不同選項。
過去,Prisma 僅支援應用層面連線策略,該策略向資料庫傳送多個查詢,並在查詢引擎內部完成所有合併和轉換為預期 JavaScript 物件結構的工作。
使用簡單、傳統的 JOIN,資料合併將委託給資料庫。然而,如上所述,存在資料冗餘問題(結果集隨著關係查詢中表數量的增加呈指數級增長)以及包含過濾和分頁的查詢複雜性問題。
為了解決這些問題,Prisma ORM 在資料庫層面實現了現代的橫向 JOIN 以及 JSON 聚合。這樣,解決查詢並將資料轉換為預期 JavaScript 物件結構所需的所有繁重工作都在資料庫層面完成。
試用並分享您的反饋
我們非常希望您能嘗試新的關係查詢載入策略。請告訴我們您的想法,並與我們分享您的反饋!
不要錯過下一篇文章!
訂閱 Prisma 簡報