DB structure summarized:
- the main table is
cases
(about 136k rows) - each case can have 0 - n referencing rows in the table
case_contacts
- each case contact references a main contact in the table
contacts
- a case contact may also reference a secondary subcontact, also in the table
contacts
- contacts have their names in
contacts.v_fullname
, which is indexed with a trigram index
The goal is to find cases where the name of a contact or subcontact contains the string "test":
SELECT c.id,
c.number
FROM cases c
JOIN case_contacts caco ON caco.case_id = c.id
JOIN contacts con_main ON con_main.id = caco.contact_id
LEFT JOIN contacts con_sub ON con_sub.id = caco.subcontact_id
WHERE con_main.v_fullname ILIKE '%test%'
OR con_sub.v_fullname ILIKE '%test%'
This query (query plan) returns the correct result, but does not use the trigram index. It takes around 330ms.
Removing either of the match conditions (query plan), or having them point at the same table (query plan), removes the performance problem. Both of these use the trigram index and are executed in under 1ms, but do not solve the given task.
How can I get PostgreSQL to use my index?
I have simplified this example to the minimum necessary to demonstrate the effect. The actual query is much more complex (and partially auto-generated), so using a UNION of two queries with only one text match each would be very hard, if it's even possible.
I'm using PostgreSQL 9.5.5.
The schema is still open for modifications (to some degree).
As requested, more information about the indexes:
dbname=# \di+ *contacts*
List of relations
Schema | Name | Type | Owner | Table | Size
--------+----------------------------------+-------+-------+---------------+---------
public | case_contacts_case_id_idx | index | x | case_contacts | 4544 kB
public | case_contacts_contact_id_idx | index | x | case_contacts | 4544 kB
public | case_contacts_id_case_id_idx | index | x | case_contacts | 4544 kB
public | case_contacts_idx | index | x | case_contacts | 9608 kB
public | case_contacts_pkey | index | x | case_contacts | 4544 kB
public | case_contacts_reference_trgm_idx | index | x | case_contacts | 4960 kB
public | case_contacts_subcontact_id_idx | index | x | case_contacts | 4544 kB
public | case_contacts_type_idx | index | x | case_contacts | 6208 kB
public | case_contacts_unique_types_idx | index | x | case_contacts | 5464 kB
public | contacts_parent_id_id_idx | index | x | contacts | 456 kB
public | contacts_parent_id_idx | index | x | contacts | 360 kB
public | contacts_pkey | index | x | contacts | 360 kB
public | contacts_v_fullname_trgm_idx | index | x | contacts | 1560 kB
(13 rows)
This is how the index on contacts.v_fullname is created:
CREATE INDEX contacts_v_fullname_trgm_idx ON contacts USING GIN (v_fullname gin_trgm_ops);
OR
.\di *con_main*
and\di *con_sub*
con_main
andcon_sub
are both aliases for the same table:contacts
. I have added information about the indexes to the question.\d contacts