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.96) (envelope-from ) id 1vrvH6-00D6B1-3C for pgsql-general@arkaria.postgresql.org; Mon, 16 Feb 2026 09:52:17 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vrvH5-000cho-2k for pgsql-general@arkaria.postgresql.org; Mon, 16 Feb 2026 09:52:15 +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.96) (envelope-from ) id 1vrvD3-0003Xu-02 for pgsql-general@lists.postgresql.org; Mon, 16 Feb 2026 09:48:05 +0000 Received: from mail-ed1-x535.google.com ([2a00:1450:4864:20::535]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1vrufQ-00000000rKs-2flC for pgsql-general@lists.postgresql.org; Mon, 16 Feb 2026 09:13:22 +0000 Received: by mail-ed1-x535.google.com with SMTP id 4fb4d7f45d1cf-658b6757f7fso5843242a12.1 for ; Mon, 16 Feb 2026 01:13:21 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1771233200; cv=none; d=google.com; s=arc-20240605; b=WI7Y4AwRLwL0V9JVXzou40dgtEb+oQ91wk6uD31YC35lJ+ev5kTQ7lh8SS5m8UxWNW ptmjNjeTuE5wXB6+j2nb4wdmQZe7RWm3nA1LF/GUU8AqGH4exflgwFlZ1aE8B+71xu6W 0sPQk4wEZM5Ecl/K3NfVe8zTTLMLkJg5BQwUXzP3zVfGhfPJtxqlfwVWYjntFZbDqxdT +CiluEkqYx6TNMKSHfYz/t8HRon2MLVuU0Ja91CXSfMHcUjskneoOMPNukOk1pH1TV3/ W+zfuv1KVF8L5afNXkIMdC+VgwU1FtAKLeYZ+pdhqrwB2uogYbdKi3onpg+xLSSo55AT gkKw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:dkim-signature; bh=sJ/4b+v5Rwch7vGby0FYhyKsGMusQDt55Nf+x/SsXlY=; fh=ODBInZsG+07HVd7yaL5XpHz07CSgSbwZB/+XsTYLtsc=; b=TseLsHijc/cgn+do7Oxcc7tDeJ2nLro116owc2C2SFJCNAzYy6R8YKINTTXAh7ywKc spmXV/2dfD3JKKwnJc5db/5Y6+dKvO2+C2EPoMe1e/yuhIbCGSHofzLgRxbFBVcqYeH6 lDIqnH+fzMsVjqsK0inuRJFl4k/s3kcZLbHDl1ItZ90hfmqny3oULZyCIegEeHjhE+Fz 7XTCyb8PDuzyLAoOy5l7FPST62mRpu5d7eBTb5Ar0cyOcjPOCAvuCvIGb2CMbS+Y8/sA GYptLJyffXVHGphv/PAwzuEn0Dt6xmaQRVb36ipzGrS335zx6vW0JktcpXjUfpcT3fHw 3Klg==; darn=lists.postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1771233200; x=1771838000; darn=lists.postgresql.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=sJ/4b+v5Rwch7vGby0FYhyKsGMusQDt55Nf+x/SsXlY=; b=POEoZ4M+XpM74VmLZtp/WMiDphoKc1DstJFM6+gQZzDQOpa/51COHG6VFPqbO9ui10 SeSwcZQdcE/5REC0vv74mO4xKyj8iYErViwcwIOZ9A6Q/cq88xDlBrI6+FjzSFkkzCeF fNXqwsvShxY9Ggkfx+DhoPYep/X1tkLdJoZRdUeYia2lVrhTvIHoWPpsW3mamW/efKI6 ShHcZVmLsQKxsAt/aNKUYgNXek6FaaokhVemg+u7Izpe3wi1F4egPP8m4C52Sygw2avF SxbPLSSYyTesq8AvRPcxtXOlthO6w/hDkq+iSb9YXZmqeW/5evZQje82vNZf0GS50549 bxOA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771233200; x=1771838000; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=sJ/4b+v5Rwch7vGby0FYhyKsGMusQDt55Nf+x/SsXlY=; b=qlg3nzYadUD8FaDpilIcl+yNEAxIHUgtKXK+f/pak5Zzt/zy55Crg7KKwNazlxDBNA OL/GuW8MDjrknSwG8oNMOF2BEJk1tlk7Hnu+bLJn4bxb1spCsDtH+BSZX6bSAk9FEvx1 ExCjAtXlOnYKg1j8rwOqsplij9fjA5GS/7zKv70YMn7ZMlUUQ3jsC8rW+5HF6S4RpdWe hdrDwSFmAMW2urojx/6akrKAuW06Vc2cgfe7BhDdIKfi4c5vRlxUTK8cfupiuGH1Ntkm 1XWzl3CbkKrLKLrOxKk+3LlsrxBTnSAHdb1O1KavEq1A7FekaolZhvviMWKjHOYKCEgD eHiw== X-Gm-Message-State: AOJu0YwsUVMOdzqb4HxHFiKKgwFbu6WLOz7j23vvPJBJ/trCkBGzbdhF 65OFOhihcnDP5Jnp1p9gQHjJUd/LWu+OjhmiPE5DnO77IJL4+pOjYo6kaPIXRYDl1hFD2QJETFC 5xiw9UsKb35O+UN1LF3oG4gW855KZods= X-Gm-Gg: AZuq6aItCKsTAkOlmiE4SudXx4fu45H8OaTiJrS6ezPTAd1j+jpZGEFGUqzQE1x20a3 P0dTjYLk1FeHIK697FswZJQmLtvV8VVylimqal+9ahZkymRqgwgSVrucpQqJJ3kQSfBYRnrbw/C /EG/Lb1mNORWIBfGZ12JBteN6GgRICjyVCv8N9GcciujUGk5X1bOM4AAC24wJe1g9lMAxFaYWmx PhHVs1oJm7Ei28x6Mufb0R7Ri5mtO8GzUAw31fhjacWZSoh+VuEjOYFrIYJNznFdesH8IOaUk5K sW0gIlthLFVkTeDSTZFxVx7gkO4ox/DYwucssajTWg== X-Received: by 2002:a05:6402:1210:b0:65b:a5cd:a148 with SMTP id 4fb4d7f45d1cf-65bc426147emr2738569a12.7.1771233199756; Mon, 16 Feb 2026 01:13:19 -0800 (PST) MIME-Version: 1.0 References: <818d0359d8b629a80b55b2e068dab958fc8e0a2a.camel@cybertec.at> In-Reply-To: <818d0359d8b629a80b55b2e068dab958fc8e0a2a.camel@cybertec.at> From: yudhi s Date: Mon, 16 Feb 2026 14:43:03 +0530 X-Gm-Features: AaiRm52z4H96gNmi5VFhrEHQcpwhSemr61KbtDeijwYyOZgPGB3yKLv9qqimftE Message-ID: Subject: Re: Question on execution plan and suitable index To: Laurenz Albe , Ron Johnson , Adrian Klaver , Nisarg Patel Cc: pgsql-general Content-Type: multipart/alternative; boundary="000000000000afae00064aed5d98" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --000000000000afae00064aed5d98 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Mon, Feb 16, 2026 at 2:29=E2=80=AFPM Laurenz Albe wrote: > On Mon, 2026-02-16 at 00:34 +0530, yudhi s wrote: > > It's postgres version 17. We are having a critical UI query which runs > for ~7 seconds+. The requirement is to bring down the response time withi= n > ~1 sec. Now in this plan , If i read this correctly, the below section is > consuming a significant amount of resources and should be addressed. i.e. > "Full scan of table "orders" and Nested loop with event_audit_log table". > > > > Below is the query and its complete plan:- > > > https://gist.github.com/databasetech0073/f564ac23ee35d1f0413980fe4d00efa9 > > > > I am a bit new to the indexing strategy in postgres. My question is, > what suitable index should we create to cater these above? > > > > 1)For table event_audit_log:- Should we create composite Index on colum= n > (request_id,created_at,event_comment_text) or should we create the coveri= ng > index i.e. just on two column (request_id,created_at) with "include" clau= se > for "event_comment_text". How and when the covering index indexes should = be > used here in postgres. Want to understand from experts? > > 2)Similarly for table orders:- Should we create a covering index on > column (entity_id,due_date,order_type) with include clause > (firm_dspt_case_id). Or just a composite index > (entity_id,due_date,order_type). > > 3)Whether the column used as range operator (here created_at or > due_date) should be used as leading column in the composite index or is i= t > fine to keep it as non leading? > > > > -> > *Nested Loop (cost=3D50.06..2791551.71 rows=3D3148 width=3D19) (actual > time=3D280.735..7065.313 rows=3D57943 loops=3D3) > Buffers: shared hit= =3D10014901* > > -> Hash Join (cost=3D49.49..1033247.35 rows=3D36729 width=3D8) (act= ual > time=3D196.407..3805.755 rows=3D278131 loops=3D3) > > Hash Cond: ((ord.entity_id)::numeric =3D e.entity_id) > > Buffers: shared hit=3D755352 > > -> Parallel Seq Scan on orders ord (cost=3D0.00..1022872.54 > rows=3D3672860 width=3D16) (actual time=3D139.883..3152.627 rows=3D294467= 1 loops=3D3) > > Filter: ((due_date >=3D '2024-01-01'::date) AND (due_date <=3D > '2024-04-01'::date) AND (order_type =3D ANY ('{TYPE_A,TYPE_B}'::text[]))) > > Rows Removed by Filter: 6572678 > > Buffers: shared hit=3D755208 > > You are selecting a lot of rows, so the query will never be really cheap. > But I agree that an index scan should be a win. > > If the condition on "order_type" is always the same, a partial index is > ideal: > > CREATE INDEX ON orders (due_date) WHERE order_type IN ('TYPE_A', > 'TYPE_B'); > > Otherwise, I'd create two indexes: one on "order_type" and one on > "due_date". > > > Version is 17.7. Below is the table definitions as i pulled from Dbeaver tool:- https://gist.github.com/databasetech0073/f22d95de18dc3f1fa54af13e7fd2ce9e The Order_type will be TYPE_A and TYPE_B in most of the cases. And below is the distribution. So , it looks like the index on this column will not help much. Correct me if I'm wrong. I am wondering why the already existing index on column "due_date" of table "order" is not getting used by the optimizer? Should we also add the column "entity_id" to the index too? TYPE_A 25 Million TYPE_B 2 Million TYPE_C 700K TYPE_D 200K TYPE_E 6k And, Yes there are differences in data types of the "entity_id" for columns of table "order" and "entity". We need to fix that after analyzing the data= . Also the highlighted Nested loop above shows ~10M shared hits (which will be ~70GB+ if we consider one hit as an 8K block). So does that mean , apart from the Full scan on the "order" table , the main resource consuming factor here is the scanning of "event_audit_log". And what is the best way to improve this? Currently this table is getting scanned through an unique index on column "request_id". Regards Yudhi --000000000000afae00064aed5d98 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable


On Mon, Feb 16,= 2026 at 2:29=E2=80=AFPM Laurenz Albe <laurenz.albe@cybertec.at> wrote:
On Mon, 2026-02-16 at 00:34 +0530, yudhi= s wrote:
> It's postgres version 17. We are having a critical UI query which = runs for ~7 seconds+. The requirement is to bring down the response time wi= thin ~1 sec. Now in this plan , If i read this correctly, the below section= is consuming a significant amount of resources and should be addressed. i.= e. "Full scan of table "orders" and Nested loop with event_a= udit_log table".
>
> Below is the query and its complete plan:-=C2=A0
> https://gist.github.co= m/databasetech0073/f564ac23ee35d1f0413980fe4d00efa9
>
> I am a bit new to the indexing strategy in postgres. My question is, w= hat suitable index should we create to cater these above?
>
> 1)For table event_audit_log:- Should we create composite Index on colu= mn (request_id,created_at,event_comment_text) or should we create the cover= ing index i.e. just on two column (request_id,created_at) with "includ= e" clause for "event_comment_text". How and when the coverin= g index indexes should be used here in postgres. Want to understand from ex= perts?=C2=A0
> 2)Similarly for table orders:- Should we create a covering index on co= lumn (entity_id,due_date,order_type) with include clause (firm_dspt_case_id= ). Or just a composite index (entity_id,due_date,order_type).
> 3)Whether the column used as range operator (here created_at or due_da= te) should be used as leading column in the composite index or is it fine t= o keep it as non leading?
>
> -> =C2=A0Nested Loop =C2=A0(cost=3D50.06..2791551.71 rows=3D3148= width=3D19) (actual time=3D280.735..7065.313 rows=3D57943 loops=3D3)
> =C2=A0=C2=A0Buffers: shared hit=3D10014901

> =C2=A0=C2=A0-> =C2=A0Hash Join =C2=A0(cost=3D49.49..1033247.35 rows= =3D36729 width=3D8) (actual time=3D196.407..3805.755 rows=3D278131 loops=3D= 3)
> =C2=A0Hash Cond: ((ord.entity_id)::numeric =3D e.entity_id)
> =C2=A0Buffers: shared hit=3D755352
> =C2=A0-> =C2=A0Parallel Seq Scan on orders ord =C2=A0(cost=3D0.00..= 1022872.54 rows=3D3672860 width=3D16) (actual time=3D139.883..3152.627 rows= =3D2944671 loops=3D3)
> =C2=A0=C2=A0Filter: ((due_date >=3D '2024-01-01'::date) AND= (due_date <=3D '2024-04-01'::date) AND (order_type =3D ANY (= 9;{TYPE_A,TYPE_B}'::text[])))
> =C2=A0=C2=A0Rows Removed by Filter: 6572678
> =C2=A0=C2=A0Buffers: shared hit=3D755208

You are selecting a lot of rows, so the query will never be really cheap. But I agree that an index scan should be a win.

If the condition on "order_type" is always the same, a partial in= dex is ideal:

=C2=A0 =C2=A0CREATE INDEX ON orders (due_date) WHERE order_type IN ('TY= PE_A', 'TYPE_B');

Otherwise, I'd create two indexes: one on "order_type" and on= e on "due_date".



Version is 17.7. Below is the tabl= e definitions as i pulled from Dbeaver tool:-


The Order_type will be TYPE_A and = TYPE_B in most of the cases. And below is the distribution. So , it looks l= ike the index on this column will not help much. Correct me if I'm wron= g. I am wondering why the already=C2=A0existing index on column "due_d= ate" of table "order" is not getting used by the optimizer? = Should we also add the column "entity_id" to the index too?
=

TYPE_A=C2=A0 25 Million
TYPE_B=C2=A0 2 Millio= n
TYPE_C=C2=A0 700K
TYPE_D=C2=A0 200K
TYPE_E= =C2=A0 6k

And, Yes there are differences in data t= ypes of the "entity_id" for columns of table "order" an= d "entity". We need to fix that after analyzing the data.

Also the highlighted Nested loop above shows ~10M shared = hits (which will be ~70GB+ if we consider one hit as an 8K block). So does = that mean , apart from the Full scan on the "order" table , the m= ain resource consuming factor here is the scanning of "event_audit_log= ". And what is the best=C2=A0way to improve this? Currently this table= is getting scanned through an unique index on column "request_id"= ;.

Regards
Yudhi

=C2=A0
--000000000000afae00064aed5d98--