---
title: "Postgres 函式與 Non-sargable 查詢"
description: "在 where 子句中使用 postgres 函式可能會讓查詢變成 non-sargable。"
canonical: https://oozou.com/til/postgres-functions-non-sargable-queries-48
locale: zh-Hant
date: 2020-10-13
topic: ruby-on-rails
---

# Postgres 函式與 Non-sargable 查詢

> 在 where 子句中使用 postgres 函式可能會讓查詢變成 non-sargable。

Published: 2020-10-13 · Topic: [ruby on rails](https://oozou.com/zh-Hant/til-topics/ruby-on-rails) ([markdown](https://oozou.com/zh-Hant/til-topics/ruby-on-rails.md))

在 *where* 子句中使用 postgres 函式可能會讓查詢變成 *non-sargable*。

**資料庫結構**

*tracks* has\_many *artists*

**Non-Sargable 查詢**

使用 *LOWER* 函式會讓 DBMS 引擎無法使用索引。

*Track.joins(:artists).where('LOWER(tracks.display\_name) LIKE ?', "eric clapton%").explain*

```
Gather  (cost=1000.85..56714.32 width=4061)
   Workers Planned: 2
   ->  Nested Loop  (cost=0.85..55713.62 rows=3 width=4061)
         ->  Nested Loop  (cost=0.42..55708.37 rows=3 width=4069)
               ->  Parallel Seq Scan on tracks  (cost=0.00..55365.17 rows=41 width=4061)
                     Filter: (lower((display_name)::text) ~~* 'eric clapton%'::text)
               ->  Index Scan using index_artist_relations_on_artist_item_type_and_artist_item_id on artist_relations  (cost=0.42..8.36 rows=1 width=16)
                     Index Cond: (((artist_item_type)::text = 'Track'::text) AND (artist_item_id = tracks.id))
         ->  Index Only Scan using idx_35952_primary on artists  (cost=0.43..1.75 rows=1 width=8)
               Index Cond: (id = artist_relations.artist_id)
```

**Sargable 查詢**

移除 *LOWER* 函式後，DBMS 引擎就能使用索引，執行速度也更快。

*Track.joins(:artists).where('tracks.display\_name ILIKE ?', "eric clapton%").explain*

```
Nested Loop  (cost=1497.60..2695.43 width=4061)
   ->  Nested Loop  (cost=1497.17..2684.94 rows=6 width=4069)
         ->  Bitmap Heap Scan on tracks  (cost=1496.75..1873.05 rows=97 width=4061)
               Recheck Cond: ((display_name)::text ~~* 'eric clapton%'::text)
               ->  Bitmap Index Scan on index_tracks_on_display_name  (cost=0.00..1496.73 rows=97 width=0)
                     Index Cond: ((display_name)::text ~~* 'eric clapton%'::text)
         ->  Index Scan using index_artist_relations_on_artist_item_type_and_artist_item_id on artist_relations  (cost=0.42..8.36 rows=1 width=16)
               Index Cond: (((artist_item_type)::text = 'Track'::text) AND (artist_item_id = tracks.id))
   ->  Index Only Scan using idx_35952_primary on artists  (cost=0.43..1.75 rows=1 width=8)
         Index Cond: (id = artist_relations.artist_id)
```

# Sargable 與 Non-Sargable

Sargable 查詢是指可以利用索引來加速的查詢。Sargable 查詢讓 DBMS 引擎能執行 index seek，比掃描整張資料表快得多。Non-sargable 查詢無法使用索引，所以比較慢。「sargable」一詞來自「Search ARGument ABLE」，意思是 DBMS 引擎可以利用索引來最佳化查詢。理解 sargable 與 non-sargable 的差異，是最佳化資料庫的關鍵。

# Non-Sargable 查詢

Non-sargable 查詢可能對查詢效能造成很大影響，在大型資料集上尤其明顯。當查詢是 non-sargable 時，DBMS 引擎必須進行 full table scan 或 index scan，兩者都是很重的操作。這可能導致執行時間變慢、CPU 使用率升高，以及整體系統效能不佳。找出並最佳化 non-sargable 查詢，是改善資料庫效率與回應速度的關鍵。

# WHERE 子句中的函式

在 WHERE 子句中使用函式可能會讓查詢變成 non-sargable。這是因為當函式套用在 WHERE 子句中的欄位上時，DBMS 引擎無法利用索引來最佳化查詢。舉例來說，考慮查詢 SELECT *FROM table WHERE UPPER(column) = 'VALUE'。這是 non-sargable 的，因為 UPPER 函式套用在欄位上，導致索引無法使用。要讓這個查詢變成 sargable，可以移除函式並把查詢改寫為 SELECT* FROM table WHERE column = 'VALUE'。這樣 DBMS 引擎就能使用索引，查詢也會更快。

# 索引與查詢最佳化

索引是查詢最佳化的關鍵，因為它讓 DBMS 引擎能快速找到資料，進而加速查詢。但只有在查詢是 sargable 時才能使用索引。如果查詢是 non-sargable，索引就派不上用場，查詢也會變慢。所以請在 WHERE 子句用到的欄位上建立索引，並確保查詢是 sargable。這會大幅提升資料庫的效能。

# 轉換 Non-Sargable 述詞

把 non-sargable 述詞轉換為 sargable 是查詢最佳化的一個步驟。透過改寫查詢，移除任何妨礙索引使用的函式或運算，就能把 non-sargable 述詞轉換為 sargable。例如查詢 SELECT *FROM table WHERE YEAR(date) = 2022 是 non-sargable 的，因為 YEAR 函式套用在 date 欄位上。要讓這個查詢變成 sargable，你可以把它改寫為 SELECT* FROM table WHERE date >= '2022-01-01' AND date \< '2023-01-01'。改寫後的查詢是 sargable 的，因為 DBMS 引擎可以利用 date 欄位上的索引來最佳化查詢，速度也會更快。

---

Contact OOZOU: hello@oozou.com · +66 2544 0000 · https://oozou.com/zh-Hant/contact

More for AI agents: https://oozou.com/llms.txt · every page has a markdown twin at its URL plus ".md"
