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 1vH5EO-00DNRC-9J for pgsql-performance@arkaria.postgresql.org; Thu, 06 Nov 2025 19:01:12 +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 1vH5EM-009sST-Tg for pgsql-performance@arkaria.postgresql.org; Thu, 06 Nov 2025 19:01:10 +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 1vH5EM-009sRd-Hk for pgsql-performance@lists.postgresql.org; Thu, 06 Nov 2025 19:01:10 +0000 Received: from mail-ed1-x52b.google.com ([2a00:1450:4864:20::52b]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1vH5EG-005lpU-1V for pgsql-performance@lists.postgresql.org; Thu, 06 Nov 2025 19:01:09 +0000 Received: by mail-ed1-x52b.google.com with SMTP id 4fb4d7f45d1cf-640ca678745so2394305a12.2 for ; Thu, 06 Nov 2025 11:01:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pgmustard.com; s=google; t=1762455663; x=1763060463; darn=lists.postgresql.org; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=0ZE9d/jEan3J0U05e9TzRhjJRtdvsj1+H78Obd/FR0Y=; b=PBH98CdB/myzDvVreH3luyIpSU8NOlA7DPE9Cpq7yl/Mn8LHelD3qgGONiJqnz92N2 /6KG2/pG4xIRJLZGGIAT1r/j7ZMGqSniDSApXUkBjByzRhyy9sNUd0XvEmrZgiJp9Nol jYrjG9i0hxdW19QbjcqIwa6lZQkhi9ZQTkYQd/uuw2XioinC9Ih2B1n/9I2Ofn8yd08+ SORF9uZMHYV738T3BvpLUxgrxRN2CUgDwHze9XwLKhiLNs3onZVKHkugY3sgfQhAffn5 gdFBHaJiBxGEh3rYe5zrS6M5HXAmRlby7ISVNeR60Y0cWSxCn+pQgcik+quZkkG5yo4D xv1Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762455663; x=1763060463; h=to:subject:message-id:date:from:mime-version:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=0ZE9d/jEan3J0U05e9TzRhjJRtdvsj1+H78Obd/FR0Y=; b=KJTvOmyBclCFJ+hzLtPYoxicr3xncOsd5MOBguoc1ihGmbMxoyAy5Ubm4fm8vMpfXL +NP+nYaWXZzYB4jEZL/GhvvB2utAmMGej5JfPj6PDV6RLiF+GDzSz1LRtblEb81MUv03 lMBygLaiU73SkAAl2c7/XdkD1bkqOMvLYmwFrGLkLovUICys7zRb776t9yOE2Tdeo2tY 6IbDUdkkrSj2cdIxusuWAWHmK03y3uoNYbi3wOppC2uEbz8lXYM/5ZNdB063S8UEvw0s jiui/WseGQt9dIwHczEsrT8JbyJWNik9kNF0WYF01K+m8D2SORT5/PL2keLAk00FdCEH mJJA== X-Gm-Message-State: AOJu0YyKHBvbYLcy3XOB4mfs2FK2U+ze2/UrpiSzgfrgdX81eY8Cv3jG Q52mq9ZWtW08U9jxD10VSMI1VXtR/B2vSm5/g6HumSKUtXRZkymUtV+/fptaaeO/7JGnbhVFFZa sOPJAJYlY1tRqO4M4EvfVWQs7gU22ozCUvo1vMA3gWlmCr0Teo10Bdur2MOcv X-Gm-Gg: ASbGnct/lNXvr2YwzWAcTHLHVMNWWMTywsCZUqotAf7rhKeVMX+bw6yqyP0KCzOcwHg iZaRqrypnFzZBTMItGvi/qzs63gXtc9pBuFQI4Su/mjC4wwnAolPqPYqmzw0bBRok3BXY6dnGve cNaW+0Cz46H02DI82bzHilasEHHkvHlwM9KgGLj/YwbtXogvUGMQ+U5XVwjZWpe7lWSKxV2JMQQ ao7OF0BRpvSxftljI85wwcxtSkvHmX1DUBIfYA2RzJR46gpCTsk5cKQp5Ofc8w2gswswTju X-Google-Smtp-Source: AGHT+IE/+hf/gkO4gzYR9WokXjbSkCGbQTwstomj27qXPBeYI/lT4x6p+VeHGNHPn9rBjLqoa3FerVadDxZtmlgG74U= X-Received: by 2002:a17:906:6a08:b0:b72:b289:6de3 with SMTP id a640c23a62f3a-b72c0d6f5e8mr27877466b.58.1762455662530; Thu, 06 Nov 2025 11:01:02 -0800 (PST) MIME-Version: 1.0 From: Michael Christofides Date: Thu, 6 Nov 2025 19:00:46 +0000 X-Gm-Features: AWmQ_bn7C4ogLtTzSv6a3Earv14HUKAeinICIG0_lZa8IEPEaTUFbRqKKAY8QFs Message-ID: Subject: Index Searches higher than expected for skip scan To: pgsql-performance@lists.postgresql.org Content-Type: multipart/alternative; boundary="000000000000b2a1dd0642f1af88" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --000000000000b2a1dd0642f1af88 Content-Type: text/plain; charset="UTF-8" Hi folks, I'm trying to understand the new Index Searches field in Postgres 18 explain analyze output. I've put together a super simple test case (below) expecting a skip scan with 2 Index Searches, one for each value in the leading (boolean) column of the index. In reality, instead of 2 Index Searches, I got 4 (query plan below). I also experimented with a leading column with 5 distinct values (expecting 5 searches), and got 7. I've not included that below, for brevity. I suspect I'm missing something obvious in either my understanding or my setup, but wondered why this might be happening? All the best, Michael CREATE TABLE example ( integer_field bigint NOT NULL, boolean_field bool NOT NULL); INSERT INTO example (integer_field, boolean_field) SELECT random () * 10_000, random () < 0.5 FROM generate_series(1, 100_000); CREATE INDEX bool_int_idx ON example (boolean_field, integer_field); VACUUM ANALYZE example; EXPLAIN (ANALYZE, BUFFERS, VERBOSE, SETTINGS) SELECT boolean_field FROM example WHERE integer_field = 5432; QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------- Index Only Scan using bool_int_idx on public.example (cost=0.29..13.04 rows=10 width=1) (actual time=0.230..0.274 rows=5.00 loops=1) Output: boolean_field Index Cond: (example.integer_field = 5432) Heap Fetches: 0 Index Searches: 4 Buffers: shared hit=9 Planning Time: 0.240 ms Execution Time: 0.323 ms --000000000000b2a1dd0642f1af88 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi folks,

I'm trying to = understand the new Index Searches field in Postgres 18 explain analyze outp= ut. I've put together a super simple test case (below) expecting a skip= scan with 2 Index Searches, one for each value in the leading (boolean) co= lumn of the index.

In reality, instead of 2 Index = Searches, I got 4 (query plan below). I also experimented with a leading co= lumn with 5 distinct values (expecting 5 searches), and got 7. I've not= included that below, for brevity.

I suspect I'= ;m missing something obvious in either my understanding or my setup, but wo= ndered why this might be happening?

All the best,<= br>Michael


CREATE TABLE example (=C2=A0 =C2=A0integer_field bigint NOT NULL,
=C2=A0 =C2=A0boolean_field = bool NOT NULL);

INSERT INTO example (integer_field, boolean_field)=C2=A0 =C2=A0SELECT random () * 10_000,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 random () < 0.5
=C2=A0 =C2=A0FROM =C2=A0 generate_series(1, 10= 0_000);
=C2=A0 =C2=A0
CREATE INDEX bool_int_idx
=C2=A0 =C2=A0ON e= xample (boolean_field, integer_field);

VACUUM ANALYZE ex= ample;

EXPLAIN (ANALYZE, BUFFERS, VERBOSE, SETTINGS)
SELECT= boolean_field FROM example WHERE integer_field =3D 5432;

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 QUERY PLAN =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0
------------------------------------------------------------= ---------------------------------------------------------------------------=
=C2=A0Index Only Scan using bool_int_idx on public.example =C2=A0(cost= =3D0.29..13.04 rows=3D10 width=3D1) (actual time=3D0.230..0.274 rows=3D5.00= loops=3D1)
=C2=A0 =C2=A0Output: boolean_field
=C2=A0 =C2=A0Index Con= d: (example.integer_field =3D 5432)
=C2=A0 =C2=A0Heap Fetches: 0
=C2= =A0 =C2=A0Index Searches: 4
=C2=A0 =C2=A0Buffers: shared hit=3D9
=C2= =A0Planning Time: 0.240 ms
=C2=A0Execution Time: 0.323 ms
--000000000000b2a1dd0642f1af88--