OOZOU
ติดต่อเรา
กลับไปหน้า TIL
ruby on rails

ฟังก์ชัน Postgres กับ Query แบบ Non-sargable

13 ตุลาคม 2563

การใช้ฟังก์ชันของ postgres ใน where clause อาจทำให้ query กลายเป็น non-sargable

โครงสร้างฐานข้อมูล

tracks has_many artists

Query แบบ Non-Sargable

การใช้ฟังก์ชัน LOWER ทำให้ DBMS engine ใช้ index ไม่ได้

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)

Query แบบ Sargable

เมื่อเอาฟังก์ชัน LOWER ออก DBMS engine ก็ใช้ index ได้ ทำให้รันได้เร็วขึ้น

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

query แบบ sargable คือ query ที่ใช้ index ช่วยให้ทำงานเร็วขึ้นได้ โดยเปิดทางให้ DBMS engine ทำ index seek ซึ่งเร็วกว่าการ scan ทั้งตารางมาก ส่วน query แบบ non-sargable ใช้ index ไม่ได้ จึงช้ากว่า คำว่า “sargable” มาจาก “Search ARGument ABLE” หมายความว่า DBMS engine สามารถใช้ index มา optimize query ได้ การเข้าใจความแตกต่างระหว่าง sargable กับ non-sargable คือกุญแจสำคัญในการ optimize ฐานข้อมูลของคุณ

Query แบบ Non-Sargable

query แบบ non-sargable ส่งผลกระทบต่อ performance ของ query ได้มาก โดยเฉพาะกับชุดข้อมูลขนาดใหญ่ เมื่อ query เป็น non-sargable ตัว DBMS engine ต้องทำ full table scan หรือ index scan ซึ่งทั้งสองอย่างเป็นงานหนัก ผลคือรันช้าลง ใช้ CPU สูงขึ้น และ performance โดยรวมของระบบแย่ลง การหาและ optimize query แบบ non-sargable จึงเป็นหัวใจของการทำให้ฐานข้อมูลมีประสิทธิภาพและตอบสนองได้ดีขึ้น

ฟังก์ชันใน WHERE Clause

การใช้ฟังก์ชันใน WHERE clause อาจทำให้ query กลายเป็น non-sargable ที่เป็นแบบนี้เพราะ DBMS engine ไม่สามารถใช้ index มา optimize query ได้เมื่อมีฟังก์ชันถูกใช้กับคอลัมน์ใน WHERE clause ตัวอย่างเช่น query SELECT FROM table WHERE UPPER(column) = 'VALUE' เป็น non-sargable เพราะฟังก์ชัน UPPER ถูกใช้กับคอลัมน์ index จึงถูกใช้ไม่ได้ ถ้าอยากให้ query นี้เป็น sargable ก็เอาฟังก์ชันออกแล้วเขียนใหม่เป็น SELECT FROM table WHERE column = 'VALUE' จากนั้น DBMS engine ก็จะใช้ index ได้และ query จะเร็วขึ้น

Index กับการ Optimize Query

index คือหัวใจของการ optimize query เพราะช่วยให้ DBMS engine หาข้อมูลได้อย่างรวดเร็ว query จึงเร็วขึ้นตาม แต่ index จะถูกใช้ได้ก็ต่อเมื่อ query เป็น sargable ถ้า query เป็น non-sargable ตัว index จะถูกใช้ไม่ได้และ query จะช้าลง ดังนั้นให้สร้าง index บนคอลัมน์ที่ใช้ใน WHERE clause และตรวจให้แน่ใจว่า query เป็น sargable แล้ว performance ของฐานข้อมูลคุณจะดีขึ้นแบบเห็นได้ชัด

การแปลง Predicate แบบ Non-Sargable

การแปลง predicate แบบ non-sargable ให้เป็น sargable คือหนึ่งในขั้นตอนของการ optimize query โดย predicate แบบ non-sargable แปลงเป็น sargable ได้ด้วยการเขียน query ใหม่เพื่อเอาฟังก์ชันหรือการดำเนินการที่ขวางการใช้ index ออก ตัวอย่างเช่น query SELECT FROM table WHERE YEAR(date) = 2022 เป็น non-sargable เพราะฟังก์ชัน YEAR ถูกใช้กับคอลัมน์ date ถ้าอยากให้ query นี้เป็น sargable ก็เขียนใหม่เป็น SELECT FROM table WHERE date >= '2022-01-01' AND date < '2023-01-01' ซึ่ง query ที่เขียนใหม่นี้เป็น sargable เพราะ DBMS engine ใช้ index บนคอลัมน์ date มา optimize query ได้และจะเร็วขึ้น

เพิ่มเติมเกี่ยวกับ ruby on rails

ruby on rails10 กุมภาพันธ์ 2565

ใช้ Pundit Policy จัดการ permitted parameters ทั้งค่า default และแยกราย action

ในการจัดการ permitted params คุณสามารถเพิ่ม permitted\attributes และ permitted\attributes\for\#{action} ใน policy ของคุณได้ เช่น

ruby on rails31 มกราคม 2565

ใช้ `requestSubmit` สำหรับการ submit form ร่วมกับ Turbo

Turbo คอยฟัง event submit ที่เกิดขึ้นบน form ดังนั้นให้ใช้ requestSubmit เสมอเมื่อจะ submit form ด้วย JS

ruby on rails19 มกราคม 2565

ให้ Rubocop และ ESLint ignore gem ที่ bundle ไว้ใน GitHub Actions

โปรเจกต์ Rails มักติดตั้ง gem ลงใน vendor directory ใน GitHub actions เพื่อให้ cache ไว้ใช้ในรอบถัด ๆ ไปได้

มีโปรเจกต์ในใจ?

เรายินดีรับฟังสิ่งที่คุณกำลังสร้าง

เริ่มต้นสนทนาhello@oozou.com
OOZOU Logo

กรุงเทพฯ · สิงคโปร์ · ฮ่องกง

X
Facebook
Instagram
LinkedIn

บริการ

  • พัฒนาเว็บ
  • เอเจนต์ AI และ Generative AI
  • พัฒนาแอปมือถือ
  • วิศวกรรมและวิเคราะห์ข้อมูล
  • UI/UX และการออกแบบผลิตภัณฑ์
  • การเปลี่ยนผ่านสู่ดิจิทัล

บริษัท

  • เกี่ยวกับเรา
  • ร่วมงานกับเรา
  • บล็อก
  • กรณีศึกษา
  • Today I Learned
  • ติดต่อเรา

เพิ่มเติม

  • อุตสาหกรรม
  • เครือข่ายพันธมิตร
© 2026 OOZOU สงวนลิขสิทธิ์
นโยบายความเป็นส่วนตัวจรรยาบรรณนโยบาย ABAC