筆記 | PostgreSQL v12 CTEs 行為改變

Share
筆記 | PostgreSQL v12 CTEs 行為改變
Photo by Caspar Camille Rubin / Unsplash

前段時間,工作環境將 PostgreSQL v10 升級到 v15,升級後發現報表的執行效率從 1 秒變成了 2 分多鐘,也剛好變因太多,排查了很多項後才開始面對 SQL 執行計畫,同一段 SQL v10 跟 v15 有很大的差別,許多人花了一番功夫調整,速度才回到水準,接著進一步從調整方向的線索,再爬網路文章,才發現原來在 PostgreSQL v12 有一項對我們來說蠻重要的改變:CTEs 行為改變。

過去經驗裡,SQL 使用 CTE (Common Table Expression) 能將一段查詢的結果暫存起來,在主查詢語句中使用,能有提升查詢效率的效果

例如:

WITH temp AS (
SELECT col1, col2, col3 FROM big_table
WHERE col1 = ‘xxx’
)
SELECT a.col1, a.col4, b.col2, b.col3
FROM table a
INNER JOIN temp b ON b.col1=a.col1

從 PostgreSQL v12 起,CTE 的行為改變了
如果該 WITH 語句符合以下條件 (全部符合),預設會併入主查詢成為子查詢

  • 不是遞迴
  • 未使用 Volatile Function
  • 在 SQL 主查詢語句中只被引用一次

等同於以下結果

SELECT a.col1, a.col4, b.col2, b.col3
FROM table a
INNER JOIN (
SELECT col1, col2, col3 FROM big_table
WHERE col1 = ‘xxx’
) b ON b.col1=a.col1

若不符合上述條件,則會像之前一樣暫存結果
如果想要強制 CTE 暫存結果,就需在 WITH AS 語法加上 MATERIALIZED 語句,例如:

WITH temp AS MATERIALIZED(
SELECT col1, col2, col3 FROM big_table
WHERE col1 = ‘xxx’
)
SELECT a.col1, a.col4, b.col2, b.col3
FROM table a
INNER JOIN temp b ON b.col1=a.col1

反之,如果當時使用 CTE 的目的是為了讓 SQL 看起來簡化,其實可以併入子查詢
就加 NOT MATERIALIZED 的語句

如果沒有加,就代表交給 PostgreSQL 的查詢優化器來決定

延伸問題

  • 那是不是全部的 WITH 語句都加 MATERIALIZED 就可以了?
    如果過去的使用習慣都是希望將結果暫存,這麼想沒有太大問題,但建議還是認真解析 SQL 來決定。
    使用 MATERIALIZED 將結果暫存,再在主查詢中使用,主查詢的條件過濾不會影響 WITH 查詢語句的結果 (也就是筆數不會改變),加上暫存臨時表沒有index,單純把所有的 WITH 語句都加上 MATERIALIZED 有可能衍生另外的效能瓶頸,我想這是 PostgreSQL v12 針對 CTE 作行為調整的原因之一。
  • 如果SQL 語法裡有很多個 WITH 語句,加第一個就好,還是全部都加?
    MATERIALIZED 語句是跟著每一個 WITH 語句的,所以如果全部都要暫存,就全部都要加。
    進一步的理解,其實要看 WITH 語句資料量、引用次數來決定,有些 WITH 語句的運算資料量少,但是被其他的 WITH 語句引用,而引用它的 WITH 語句資料量很大,此時運算量就會隨著引用者的資料量增加,這時將被引用者加上 MATERIALIZED 就可以達到很好的效能提升效果,需要 by case 解析。

相關文章:

Read more

又搬家了:從 Zeabur 遷移到 AWS Lightsail

又搬家了:從 Zeabur 遷移到 AWS Lightsail

是的,又搬家了。 前一篇「部落格遷移紀錄」才記錄了從 WordPress 搬到 Ghost,主機也從 SugarHosts、GCP 一路輾轉到了 Zeabur。原本覺得以這個部落格的流量與使用方式來說,Zeabur 簡單方便,應該可以安穩地用上一段時間。 近期 Zeabur 發生的資安事件,剛好讓我重新思考一直沒去想想這個筆記到底放哪好的問題,能符合掛這個小站的經濟效益,我又能在有餘裕時利用主機做一些嘗試,最後決定搬到 AWS Lightsail。 選擇 Lightsail 其實沒有太多 AWS 的使用經驗,一開始在查看時,想過是不是直接用 EC2,但研究了一會,對我這種單純放部落格的小站來說,建一個 EC2 能調整的東西很多,相對也代表需要理解與管理的東西更多,殺雞何需牛刀呢.. 於是又看到了 Lightsail ,看起來比較接近我需要的樣子:一台固定規格的 VM、一個 Static IP,加上相對單純的計價方式(

By Jo
桌面上的筆電顯示程式碼,旁邊放著咖啡杯,象徵日常部署與開發工作流

[紀錄] OpenClaw 部署指定模型

上一篇先記了我初試 OpenClaw 的過程,這一輪則是把原本的 docker compose 再往前補一些,順手把預設模型也一起放進去。 這次選擇的是 Ollama,預設模型設成 minimax-m2.5:cloud。 原本以為把 .env 補好、compose 啟動,接著就能開始用了。做了才知道事情沒有我想得順利,仍然還是需要手動進 container 執行指令。 因為這次在 docker compose 想放進預設模型,所以整個配置也跟著多補了一些。原本比較單純的 OpenClaw 部署,後來變成 openclaw + ollama 的配置,讓 OpenClaw 啟動後能直接接上模型。 不過模型名稱先放進去,事情也沒這麼順。 Ollama 要使用 cloud model 得先登入。第一次啟動後,要先進到 Ollama 容器裡跑

By Jo Assistant, Jo
[紀錄] 初試 OpenClaw

[紀錄] 初試 OpenClaw

夯了很久的 OpenClaw,近期開始出現了退安裝潮,我卻正要開始嘗試使用。 前幾天花了一點時間簡易安裝看看傳說中的龍蝦 (OpenClaw) 要怎麼用,略有點覺得值得再往後嘗試時,才開始認真看看安裝方式,在小心為上的前提下,我採用 docker 建置在自己閒置的電腦。 在 docker-compose.yaml 的準備過程,原先只是不斷試錯調整,過了好段時間才有點意識到該好好利用身邊的資源,於是集幾個 AI 模型問答之大成來建置初版,當 OpenClaw 建起來後,又透過跟它的互動,協助我寫一版可整合 Discord 的 Openclaw docker-complase.yaml 自用。(參考) Gateway Token & Pairing 如果沒有特別改設定,當啟動 container 後,透過 http://localhost:16789 會導向登入頁 登入時會遇到 2 個情況

By Jo