Using postgres functions inside a where clause can make a query non-sargable.
Database Structure
tracks has_many artists
Non-Sargable Query
Using a LOWER function prevents DBMS engine from using indexes.
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 Query
Removing LOWER function allows DBMS engine to use indexes, resulting in faster execution.
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)
From us to your inbox weekly.