Subject: BTREE index: field ordering

From: Moreno Andreo <[email-masked]>
Date: Fri, 28 Mar 2025 13:38:26 +0100

Hi,
Postgres 16.4 (planning to go on 17.4)
I'm creating some indexes based on some slow query reported by logs.
These queries involve a WHERE with more than 5 fields, that are matching 
by =, <>, LIKE and IN()
I read that equality fields must be first, then the others.
Is it correct?

Based on this query

SELECT COUNT(id) AS total
                 FROM nx.tbl1
                 WHERE
             (date_order >= '2025-03-21')
             AND (date_order <= '2025-03-29')
             AND (flag = TRUE)
             AND ((
               -- (flag = TRUE)
               -- AND (((tipo <> 'C') AND (tipo <> 'V') AND (tipo <> 
'F')) OR (tipo IS NULL) OR (tipo = ''))
               (((op <> 'C') OR (op IS NULL)) OR (tipo = 'F'))
               AND (s_state IN 
('ENQ','WFR','BLQ','BLR','WFA','FRW','FRO','0000','0001'))
               AND (tiporic IS NOT NULL)
               AND (tiporic NOT LIKE '%cart%')
             ) OR (
               (tiporic LIKE '%cart%') AND (S_state <> 'CON') AND 
(s_state <> '0002') AND ((op <> 'C') OR (op IS NULL))
             ))  AND (priv IS NULL OR priv = false OR (priv = true and 
idpriv = 'TEST')));

Should the following index be correct?

CREATE INDEX IF NOT EXISTS tbl1_count_idx on nx.tbl1 USING BTREE(flag, 
tipo, op, priv, idpriv, date_order, s_state, tiporic);

Would it be better to create a separate GIN/GIST index for the field 
matched with LIKE?

Thanks in advance,
Moreno

---

From: Laurenz Albe <[email-masked]>
Date: Fri, 28 Mar 2025 14:35:05 +0100

On Fri, 2025-03-28 at 13:38 +0100, Moreno Andreo wrote:
>  These queries involve a WHERE with more than 5 fields, that are matching by =, <>, LIKE and IN() 

Fundamentally yes, but you also have to consider how selective the conditions are.
Putting a column in the index where the condition will only filter out few rows
is not going to help; such rows should be omitted from the index.

>                -- AND (((tipo <> 'C') AND (tipo <> 'V') AND (tipo <> 'F')) OR (tipo IS NULL) OR (tipo = '')) 
>                AND (s_state IN ('ENQ','WFR','BLQ','BLR','WFA','FRW','FRO','0000','0001')) 
>                (tiporic LIKE '%cart%') AND (S_state <> 'CON') AND (s_state <> '0002') AND ((op <> 'C') OR (op IS NULL)) 
>              ))  AND (priv IS NULL OR priv = false OR (priv = true and idpriv = 'TEST'))); 
>
>  CREATE INDEX IF NOT EXISTS tbl1_count_idx on nx.tbl1 USING BTREE(flag, tipo, op, priv, idpriv, date_order, s_state, tiporic); 
>  
>  Would it be better to create a separate GIN/GIST index for the field matched with LIKE? 

The ORs will be a problem. Get rid of them as much as possible by using UNION,
at least for WHERE conditions that are selective.

Yours,
Laurenz Albe

---

From: Kevin Stephenson <[email-masked]>
Date: Sat, 29 Mar 2025 07:28:46 +0000

Moreno,

You have two lines in your WHERE clause commented out, the first line is a duplicate check on flag = TRUE, and the other line involves several checks on the "tipo" column. Will the final query or set of related queries actually need to filter on the "tipo" column? You currently have "tipo" in second place in your candidate index.

Thanks,
Kevin
________________________________
From: Moreno Andreo <[email-masked]>
Sent: Friday, March 28, 2025 5:38 AM
To: PostgreSQL mailing lists <[email-masked]>
Subject: BTREE index: field ordering

Hi,

Based on this query

Thanks in advance,
Moreno