Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1u07lJ-00CTgM-Vd for pgsql-general@arkaria.postgresql.org; Wed, 02 Apr 2025 23:44:50 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1u07lI-0012uh-62 for pgsql-general@arkaria.postgresql.org; Wed, 02 Apr 2025 23:44:48 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1u07lH-0012uY-Pl for pgsql-general@lists.postgresql.org; Wed, 02 Apr 2025 23:44:47 +0000 Received: from mail-pf1-x42e.google.com ([2607:f8b0:4864:20::42e]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1u07lG-002dI4-0B for pgsql-general@lists.postgresql.org; Wed, 02 Apr 2025 23:44:46 +0000 Received: by mail-pf1-x42e.google.com with SMTP id d2e1a72fcca58-739525d4e12so256644b3a.3 for ; Wed, 02 Apr 2025 16:44:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1743637484; x=1744242284; darn=lists.postgresql.org; h=to:cc:date:message-id:subject:mime-version:from :content-transfer-encoding:from:to:cc:subject:date:message-id :reply-to; bh=NvTv2sBGgNUQfc9DnJ408butR6pAMCFiYUhC86PJpII=; b=jdiWS1H89uBYF/IoJyVlDcHQCC+WcBkr5HZZz0aBD8vLUshhzZn5WglscJqwRM6JjK YgpyyKIzhDh5sbUI2UMuVZWo+3cgGgNsAcPgg3CI5XcvpMAlFcth9iQ8Drq+Hg5YCuKj NE+a2xtogUACcI4OGP20tMOashyVPwc8Drjl4WJRSiy3o8VsTSdL7ifu9kRxJrVf6Kso npgBZof2vykuXMZ/vEryCDQl/JSOS0h7YaQON+TAGHjVI4ARZ589Nlap2CqiWho+ns0a KsKS3gNTzASXVtIJ39YDHCg+juuiqJmDhTgPA4iGquwKGqNCpPLyFRVbrsrTi0rQwTtf rvBQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1743637484; x=1744242284; h=to:cc:date:message-id:subject:mime-version:from :content-transfer-encoding:x-gm-message-state:from:to:cc:subject :date:message-id:reply-to; bh=NvTv2sBGgNUQfc9DnJ408butR6pAMCFiYUhC86PJpII=; b=ikNjsjmg97kJyoYMKorUtfVAgDio3s/Plv5fYYqrq5e1CJ2cUHwtiNAZGDY6hvXF3l cDzFj04Qp/aLORCkZDvA1m2CHrieV6hEgY52wAOoNg0qjcuLQUnew26wSs9vumaDlmDJ NweZ7Z95MnC1QZPXtRZdhIO4UXzBbrP1O9fQWQjlnZObUp2vLxYWmjb7YTWhyvGTxK6I B8cRy3Fi65LKweb8gzanu8glSsuocdLAHkb6fxc7cAZ3H1BmAyB4D/HyvvvGVM/7h76P 4a4iaE4LYxVYbOeGIpS/8B4QDXPn8VOhqPchQetElC9V5i7N9b4q4rzva3YWDkt05ADy /Ecw== X-Gm-Message-State: AOJu0YzdOBTJHiwr5JU0mGa8y+jfXP7K9Fpq4iMWGj0ar1jXM54Vbq4M 4ZhyI93I1fFG8zTcKoPkfljmSKIZ7FSN+i9cDYCmwAKqnccq1qJoplnD6g== X-Gm-Gg: ASbGnctgDZeUGgU00sV/mBDbg845jdkT69Oavc52mk0Y29VSstyiA5GrnL8ot1vddJg nYUeuaPFmrOARU+T5GUlPFVh8ldwVYCsr15xHyRq2dnMoA9fxGJIqUS4eJGFctbVvzYqhX1g8xp 9NMF6o60Pdhf2s44fY1WuGP9az1br7B6VgpIjZMyChKD6FVbopR2aQX6TdYejIPzSiRDy4eTuwN YAUKm/3+tzyRHyzIIuyBpFC8D5qNgpzjW7HfGSmt5S0/8PmU9UYTBO9HhG4uNON/Aool5Rz7IR2 HgSwO3Il1jiKCoSWV6J8EeB6AMLMBVXONKPewKxuCmKK8hqz8Aai/2W7AIUZauS9MVSInsI4mw= = X-Google-Smtp-Source: AGHT+IGbFoOqSaqHoCbS0Tk/2GaG7qkANHwQGlnjOw0sIN3n7W00ke/Y0tp98NfkUZp1B5nJ+06HKg== X-Received: by 2002:a05:6a00:2306:b0:736:5753:12f7 with SMTP id d2e1a72fcca58-739d9b6a250mr368540b3a.3.1743637484146; Wed, 02 Apr 2025 16:44:44 -0700 (PDT) Received: from smtpclient.apple ([2601:644:4003:ea90:1cb2:d1f9:2823:ddd5]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-739da0b4245sm100821b3a.152.2025.04.02.16.44.42 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 02 Apr 2025 16:44:43 -0700 (PDT) Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable From: Manikandan Swaminathan Mime-Version: 1.0 (1.0) Subject: Re: Postgres Query Plan using wrong index Message-Id: Date: Wed, 2 Apr 2025 16:44:32 -0700 Cc: pgsql-general@lists.postgresql.org To: Tom Lane X-Mailer: iPhone Mail (20G81) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk =EF=BB=BFThanks so much for your help, Tom. Sorry, I didn=E2=80=99t quite understand the answer =E2=80=94 I have a few f= ollow-up questions. Sorry, I'm new to Postgres so I am a bit ignorant here a= nd would appreciate any tips on the query planner you could give. 1) Why is the query currently picking the poorly performing index? I already= have an index on (col_a, col_b) that performs well. When I remove the separ= ate index on (col_b), it correctly uses the (col_a, col_b) index and the que= ry runs efficiently. But when both indexes are present, it chooses the slowe= r (col_b) index instead. 2) Why would the index you suggested, (col_b, col_a), perform better than (c= ol_a, col_b)? I would=E2=80=99ve expected the filter on col_a to come first,= followed by the aggregate on col_b. In my mind, it needs to find rows match= ing the col_a condition before calculating the MIN(col_b), and I assumed it w= ould traverse the B-tree accordingly. I'm more used to MySQL where I think i= t is called a "lose index scan". I must have a gap in my understanding of h= ow Postgres approaches this. Thanks for your help! 3) Why does the planner choose the better-performing (col_a, col_b) index wh= en the filter is col_a > 5000, but switch to the slower (col_b) index when t= he filter is not at the edge of the range, like col_a > 4996? For reference,= here=E2=80=99s the query plan when filtering for col_a > 5000. It uses the c= orrect index on (col_a, col_b). postgres=3D# explain analyze select min(col_b) from test_table where col_a >= 5000; =20 Aggregate (cost=3D4.46..4.46 rows=3D1 width=3D4) (actual time=3D0.008..0.0= 08 rows=3D1 loops=3D1) -> Index Only Scan using idx_col_b_a on test_table (cost=3D0.43..4.45 r= ows=3D1 width=3D4) (actual time=3D0.004..0.005 rows=3D0 loops=3D1) Index Cond: (col_a > 5000) Heap Fetches: 0 Planning Time: 2.279 ms Execution Time: 0.028 ms (6 rows) >=20 > On Apr 1, 2025, at 5:30 PM, Tom Lane wrote: > =EF=BB=BFManikandan Swaminathan writes: >> 4. When running the following query, I would expect the index "idx_col_b_= a" >> to be used: select min(col_b) from test_table where col_a > 4996. >> I have a range-based filter on col_a, and am aggregating the result with >> min(col_b). Both columns are covered by "idx_col_b_a". >=20 > They may be covered, but sort order matters, and that index has the > wrong sort order to help with this query. Try >=20 > create index on test_table(col_b, col_a); >=20 > regards, tom lane