Question

I have the following table in Postgres 9.6:

                                       Table "public.TagNotifications"
    Column    |            Type             |                            Modifiers                            
--------------+-----------------------------+-----------------------------------------------------------------
 createdAt    | timestamp with time zone    | not null default now()
 updatedAt    | timestamp with time zone    | not null default now()
 id           | integer                     | not null default nextval('"TagNotifications_id_seq"'::regclass)
 tag          | character varying(255)      | not null
 triggerId    | integer                     | 
 userId       | integer                     | not null
 comparison   | "TagNotificationComparison" | 
 setpoint     | double precision            | 
 severity     | "TagNotificationSeverity"   | 
 acknowledged | boolean                     | not null default false
 value        | double precision            | 
Indexes:
    "TagNotifications_pkey" PRIMARY KEY, btree (id)
    "TagNotificactions_userId_acknowledged_createdAt_tag_id" btree ("userId", acknowledged, "createdAt" DESC, tag, id)
    "TagNotificactions_userId_acknowledged_tag_createdAt_id" btree ("userId", acknowledged, tag, "createdAt" DESC, id)
Foreign-key constraints:
    "TagNotifications_tag_fkey" FOREIGN KEY (tag) REFERENCES "Metadata"(tag) ON UPDATE CASCADE ON DELETE CASCADE
    "TagNotifications_triggerId_fkey" FOREIGN KEY ("triggerId") REFERENCES "TagNotificationTriggers"(id) ON UPDATE CASCADE ON DELETE SET NULL
    "TagNotifications_userId_fkey" FOREIGN KEY ("userId") REFERENCES "Users"(id) ON UPDATE CASCADE ON DELETE CASCADE

I'm trying to ensure good performance for the following query (and similar queries with cursor-based pagination):

EXPLAIN
SELECT * FROM "TagNotifications"
WHERE ("userId" = 2 AND "acknowledged" = false)
ORDER BY "tag" ASC, "createdAt" DESC, "id" ASC
LIMIT 6;
                                     QUERY PLAN                                     
------------------------------------------------------------------------------------
 Limit  (cost=840.10..840.12 rows=6 width=75)
   ->  Sort  (cost=840.10..856.04 rows=6376 width=75)
         Sort Key: tag, "createdAt" DESC, id
         ->  Seq Scan on "TagNotifications"  (cost=0.00..725.81 rows=6376 width=75)
               Filter: ((NOT acknowledged) AND ("userId" = 2))
(5 rows)

Why isn't Postgres smart enough to use the index in this case? It uses the index if I include "userId" and acknowledged in ORDER BY, but this shouldn't be necessary because of the WHERE condition:

EXPLAIN
SELECT * FROM "TagNotifications"
WHERE ("userId" = 2 AND "acknowledged" = false)
ORDER BY "userId" ASC, "acknowledged" ASC, "tag" ASC, "createdAt" DESC, "id" ASC
LIMIT 6;
                                                                   QUERY PLAN                                                                   
------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.41..2.27 rows=6 width=75)
   ->  Index Scan using "TagNotificactions_userId_acknowledged_tag_createdAt_id" on "TagNotifications"  (cost=0.41..1974.27 rows=6376 width=75)
         Index Cond: (("userId" = 2) AND (acknowledged = false))
         Filter: (NOT acknowledged)
(4 rows)
Was it helpful?

Solution

It has grown smarter in v10.

I think the reason it wasn't smart enough before is described here:

https://www.postgresql.org/message-id/1788.1481605684@sss.pgh.pa.us

I don't know how to summarize that, so I'll just say "booleans in indexes are confusing"

Licensed under: CC-BY-SA with attribution
Not affiliated with dba.stackexchange
scroll top