<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: bertrand HARTWIG</title>
    <description>The latest articles on DEV Community by bertrand HARTWIG (@bertrand_hartwig_309d1958).</description>
    <link>https://dev.to/bertrand_hartwig_309d1958</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1915824%2Fb0620e35-b10d-46de-9781-576785aba401.jpg</url>
      <title>DEV Community: bertrand HARTWIG</title>
      <link>https://dev.to/bertrand_hartwig_309d1958</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bertrand_hartwig_309d1958"/>
    <language>en</language>
    <item>
      <title>PostgreSQL: Which Queries Should You Optimize First?</title>
      <dc:creator>bertrand HARTWIG</dc:creator>
      <pubDate>Thu, 14 May 2026 08:08:52 +0000</pubDate>
      <link>https://dev.to/bertrand_hartwig_309d1958/postgresql-which-queries-should-you-optimize-first-5ga</link>
      <guid>https://dev.to/bertrand_hartwig_309d1958/postgresql-which-queries-should-you-optimize-first-5ga</guid>
      <description>&lt;p&gt;When investigating PostgreSQL performance, the usual starting point is &lt;code&gt;pg_stat_statements&lt;/code&gt;. From there, many teams sort queries by &lt;code&gt;mean_exec_time&lt;/code&gt; or &lt;code&gt;total_exec_time&lt;/code&gt; and start optimizing the first rows in the list.&lt;/p&gt;

&lt;p&gt;That approach is simple, but it often leads to the wrong priorities.&lt;/p&gt;

&lt;p&gt;A query that takes five seconds but runs twice a day is not necessarily more important than a query that takes five milliseconds and runs millions of times. Conversely, a query with a high total execution time may simply be a normal core workload query, not necessarily the best optimization target.&lt;/p&gt;

&lt;p&gt;The real question is not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which query is the slowest?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which query has the highest operational impact and the clearest optimization potential?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the principle behind the query-ranking algorithm implemented in &lt;strong&gt;pgAssistant&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://beh74.github.io/pgassistant-blog/post/ranking/" rel="noopener noreferrer"&gt;Read the full post here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>postgressql</category>
      <category>performance</category>
    </item>
    <item>
      <title>Indexing every WHERE column is not PostgreSQL optimization</title>
      <dc:creator>bertrand HARTWIG</dc:creator>
      <pubDate>Mon, 11 May 2026 15:20:15 +0000</pubDate>
      <link>https://dev.to/bertrand_hartwig_309d1958/indexing-every-where-column-is-not-postgresql-optimization-3gj0</link>
      <guid>https://dev.to/bertrand_hartwig_309d1958/indexing-every-where-column-is-not-postgresql-optimization-3gj0</guid>
      <description>&lt;p&gt;One PostgreSQL indexing mistake I see often:&lt;/p&gt;

&lt;p&gt;“The query filters on A, B and C, so let’s create an index on A, B, C.”&lt;/p&gt;

&lt;p&gt;That may work, but it may also be the wrong index.&lt;/p&gt;

&lt;p&gt;For composite B-tree indexes, PostgreSQL cares about predicate type, column order, selectivity, table size, and the actual execution plan.&lt;/p&gt;

&lt;p&gt;In this post, I explain why equality predicates usually belong before range predicates, why n_distinct from statistics matters, and why a theoretically good index is useless if the planner never uses it.&lt;/p&gt;

&lt;p&gt;I also show how &lt;strong&gt;pgAssistant&lt;/strong&gt; turns this into an automated index recommendation workflow using EXPLAIN ANALYZE and planner statistics.&lt;/p&gt;

&lt;p&gt;Full write-up:&lt;br&gt;
&lt;a href="https://beh74.github.io/pgassistant-blog/post/query_advisor/" rel="noopener noreferrer"&gt;https://beh74.github.io/pgassistant-blog/post/query_advisor/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>performance</category>
    </item>
    <item>
      <title>Designing the Right PostgreSQL Index Using Query Plans and Statistics</title>
      <dc:creator>bertrand HARTWIG</dc:creator>
      <pubDate>Mon, 11 May 2026 06:04:15 +0000</pubDate>
      <link>https://dev.to/bertrand_hartwig_309d1958/designing-the-right-postgresql-index-using-query-plans-and-statistics-26d1</link>
      <guid>https://dev.to/bertrand_hartwig_309d1958/designing-the-right-postgresql-index-using-query-plans-and-statistics-26d1</guid>
      <description>&lt;p&gt;PostgreSQL index design is often misunderstood.&lt;/p&gt;

&lt;p&gt;Many developers think that creating a good index simply means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Create an index containing the columns from the WHERE clause.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In reality, efficient index design is far more nuanced.&lt;/p&gt;

&lt;p&gt;The order of columns inside a composite index matters enormously, and the best choice depends on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Predicate types (&lt;code&gt;=&lt;/code&gt;, &lt;code&gt;&amp;gt;=&lt;/code&gt;, &lt;code&gt;BETWEEN&lt;/code&gt;, &lt;code&gt;LIKE&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;Column selectivity&lt;/li&gt;
&lt;li&gt;Table size&lt;/li&gt;
&lt;li&gt;PostgreSQL planner statistics&lt;/li&gt;
&lt;li&gt;Actual execution plans&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article explains the core principles behind efficient PostgreSQL index design before showing how pgAssistant automates this process using execution plans and database statistics.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Index Design Is Difficult
&lt;/h1&gt;

&lt;p&gt;Consider the following query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ship_country&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;employee_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;order_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, several index definitions may appear reasonable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But these indexes do &lt;strong&gt;not&lt;/strong&gt; behave the same way.&lt;/p&gt;

&lt;p&gt;Choosing the correct ordering requires understanding how PostgreSQL traverses B-Tree indexes.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Fundamental Rule of B-Tree Indexes
&lt;/h1&gt;

&lt;p&gt;For PostgreSQL B-Tree indexes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Equality predicates should come first&lt;/li&gt;
&lt;li&gt;Range predicates should come last&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is the single most important rule in multi-column index design.&lt;/p&gt;




&lt;h1&gt;
  
  
  Equality Predicates Are Highly Selective
&lt;/h1&gt;

&lt;p&gt;Predicates such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;allow PostgreSQL to navigate directly to a very precise section of the index tree.&lt;/p&gt;

&lt;p&gt;In our query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;employee_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;are equality predicates.&lt;/p&gt;

&lt;p&gt;If the index begins with these columns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PostgreSQL can rapidly narrow the search space.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;customer_id = exact branch
employee_id = exact sub-branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The planner can jump almost directly to the matching rows.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Range Predicates Should Come Last
&lt;/h1&gt;

&lt;p&gt;Now consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;order_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a range predicate.&lt;/p&gt;

&lt;p&gt;Once PostgreSQL enters a range scan inside a B-Tree index, the remaining columns become far less useful for navigation.&lt;/p&gt;

&lt;p&gt;For example, with this index:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the planner must first scan all matching dates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;order_date &amp;gt;= DATE $3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which may represent a very large portion of the table.&lt;/p&gt;

&lt;p&gt;Only afterward can additional filtering occur.&lt;/p&gt;

&lt;p&gt;This usually produces significantly more index scanning.&lt;/p&gt;

&lt;p&gt;That is why range predicates are generally placed at the end of composite indexes.&lt;/p&gt;




&lt;h1&gt;
  
  
  Column Order Among Equality Predicates
&lt;/h1&gt;

&lt;p&gt;Once equality predicates are identified, the next challenge is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which equality column should come first?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer depends on selectivity.&lt;/p&gt;

&lt;p&gt;PostgreSQL exposes this information through planner statistics.&lt;/p&gt;




&lt;h1&gt;
  
  
  PostgreSQL Statistics Drive Good Index Design
&lt;/h1&gt;

&lt;p&gt;For our query, PostgreSQL statistics are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;order_date [&amp;gt;=]:
    n_distinct=814
    null_frac=0.0000
    mcv_count=100
    histogram_bounds=101

customer_id [=]:
    n_distinct=89
    null_frac=0.0000
    mcv_count=89
    histogram_bounds=0

employee_id [=]:
    n_distinct=9
    null_frac=0.0000
    mcv_count=9
    histogram_bounds=0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most important metric here is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;n_distinct
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Understanding &lt;code&gt;n_distinct&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;n_distinct&lt;/code&gt; estimates the number of distinct values in a column.&lt;/p&gt;

&lt;p&gt;Higher &lt;code&gt;n_distinct&lt;/code&gt; usually means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;higher selectivity&lt;/li&gt;
&lt;li&gt;fewer matching rows&lt;/li&gt;
&lt;li&gt;better filtering efficiency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In our example:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Column&lt;/th&gt;
&lt;th&gt;n_distinct&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;customer_id&lt;/td&gt;
&lt;td&gt;89&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;employee_id&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;customer_id&lt;/code&gt; is significantly more selective.&lt;/p&gt;

&lt;p&gt;Therefore, PostgreSQL benefits more from filtering by &lt;code&gt;customer_id&lt;/code&gt; first.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Selectivity Matters
&lt;/h1&gt;

&lt;p&gt;Imagine the table contains 1 million rows.&lt;/p&gt;

&lt;p&gt;Filtering by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;employee_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;may still leave:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1,000,000 / 9 ≈ 111,111 rows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Filtering by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;customer_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;may reduce the result to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1,000,000 / 89 ≈ 11,236 rows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Starting with the most selective equality predicate drastically reduces the search space.&lt;/p&gt;

&lt;p&gt;This improves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;index scan efficiency&lt;/li&gt;
&lt;li&gt;cache locality&lt;/li&gt;
&lt;li&gt;heap access reduction&lt;/li&gt;
&lt;li&gt;execution time&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  The Correct Index Design
&lt;/h1&gt;

&lt;p&gt;Applying these principles:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Equality predicates first&lt;/li&gt;
&lt;li&gt;Most selective equality columns first&lt;/li&gt;
&lt;li&gt;Range predicates last&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;produces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;CONCURRENTLY&lt;/span&gt;
    &lt;span class="n"&gt;pga_idx_orders_customer_id_employee_id_order_date&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ordering allows PostgreSQL to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate efficiently using exact matches&lt;/li&gt;
&lt;li&gt;Reduce scanned rows as early as possible&lt;/li&gt;
&lt;li&gt;Apply the range scan only after narrowing the search space&lt;/li&gt;
&lt;/ol&gt;




&lt;h1&gt;
  
  
  Good Index Design Also Depends on Table Size
&lt;/h1&gt;

&lt;p&gt;One of the biggest misconceptions about PostgreSQL optimization is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Indexes are always faster.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is false.&lt;/p&gt;

&lt;p&gt;For small tables, PostgreSQL often prefers a Sequential Scan (&lt;code&gt;Seq Scan&lt;/code&gt;) even when an index exists.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because using an index has overhead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;traversing the B-Tree&lt;/li&gt;
&lt;li&gt;reading index pages&lt;/li&gt;
&lt;li&gt;performing heap lookups&lt;/li&gt;
&lt;li&gt;random I/O access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For sufficiently small tables, scanning the entire table sequentially is cheaper.&lt;/p&gt;




&lt;h1&gt;
  
  
  Query Plans Matter More Than Theory
&lt;/h1&gt;

&lt;p&gt;A theoretically perfect index is useless if PostgreSQL never uses it.&lt;/p&gt;

&lt;p&gt;That is why index recommendation engines should never rely only on SQL syntax.&lt;/p&gt;

&lt;p&gt;They must also inspect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;execution plans&lt;/li&gt;
&lt;li&gt;estimated costs&lt;/li&gt;
&lt;li&gt;table statistics&lt;/li&gt;
&lt;li&gt;row estimates&lt;/li&gt;
&lt;li&gt;planner decisions&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  The Importance of Execution Plans
&lt;/h1&gt;

&lt;p&gt;The execution plan reveals how PostgreSQL actually executes a query.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="k"&gt;ANALYZE&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;may show:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Seq Scan on orders
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Index Scan using ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This distinction is critical.&lt;/p&gt;

&lt;p&gt;A query may contain filter predicates that look index-friendly, but PostgreSQL may correctly determine that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the table is too small&lt;/li&gt;
&lt;li&gt;selectivity is too low&lt;/li&gt;
&lt;li&gt;too many rows would still be scanned&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and therefore prefer a sequential scan.&lt;/p&gt;




&lt;h1&gt;
  
  
  How pgAssistant Recommends Indexes
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffue2g57y0f27kpl37uot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffue2g57y0f27kpl37uot.png" alt=" " width="800" height="620"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;pgAssistant does not simply parse SQL queries.&lt;/p&gt;

&lt;p&gt;It combines multiple sources of information:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Query Plan
&lt;/h2&gt;

&lt;p&gt;pgAssistant analyzes nodes in the query plan to identify candidate index columns.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Predicate Types
&lt;/h2&gt;

&lt;p&gt;It classifies predicates into categories:&lt;/p&gt;

&lt;h3&gt;
  
  
  Equality predicates
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="k"&gt;IN&lt;/span&gt;
&lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Range predicates
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;
&lt;span class="k"&gt;BETWEEN&lt;/span&gt;
&lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'prefix%'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Equality predicates are prioritized before range predicates.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Column Statistics
&lt;/h2&gt;

&lt;p&gt;pgAssistant uses PostgreSQL planner statistics such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;n_distinct&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;null_frac&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;most_common_vals&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;most_common_freqs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;histogram_bounds&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;to estimate column selectivity.&lt;/p&gt;

&lt;p&gt;Columns with higher selectivity are prioritized earlier in the index definition.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Table Statistics
&lt;/h2&gt;

&lt;p&gt;pgAssistant also evaluates table-level statistics, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;estimated row counts&lt;/li&gt;
&lt;li&gt;table size&lt;/li&gt;
&lt;li&gt;planner cost estimates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is extremely important because some tables are simply too small to justify an index.&lt;/p&gt;

&lt;p&gt;In these cases, recommending an index would create unnecessary maintenance overhead without improving performance.&lt;/p&gt;




&lt;h1&gt;
  
  
  How pgAssistant Recommends PostgreSQL Indexes
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Why Query Syntax Alone Is Not Enough
&lt;/h2&gt;

&lt;p&gt;A good recommendation depends on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;predicate types&lt;/li&gt;
&lt;li&gt;column selectivity&lt;/li&gt;
&lt;li&gt;table statistics&lt;/li&gt;
&lt;li&gt;planner estimates&lt;/li&gt;
&lt;li&gt;execution plans&lt;/li&gt;
&lt;li&gt;existing indexes already used by PostgreSQL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is precisely the approach implemented by pgAssistant.&lt;/p&gt;




&lt;h1&gt;
  
  
  pgAssistant Uses Execution Plans First
&lt;/h1&gt;

&lt;p&gt;pgAssistant starts from the PostgreSQL execution plan.&lt;/p&gt;

&lt;p&gt;It analyzes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ANALYZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FORMAT&lt;/span&gt; &lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is extremely important because the execution plan reveals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;whether PostgreSQL uses a &lt;code&gt;Seq Scan&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;whether an &lt;code&gt;Index Scan&lt;/code&gt; already exists&lt;/li&gt;
&lt;li&gt;whether residual filtering still occurs after index access&lt;/li&gt;
&lt;li&gt;whether planner row estimations are inaccurate&lt;/li&gt;
&lt;li&gt;whether a composite index could reduce heap filtering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This avoids many false-positive recommendations.&lt;/p&gt;

&lt;p&gt;A query may look index-friendly while PostgreSQL is already using the optimal access path.&lt;/p&gt;




&lt;h1&gt;
  
  
  pgAssistant Analyzes Existing Access Paths
&lt;/h1&gt;

&lt;p&gt;The advisor first determines how PostgreSQL currently accesses the table.&lt;/p&gt;

&lt;p&gt;Examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Seq Scan
Index Scan
Index Only Scan
Bitmap Heap Scan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This distinction is critical.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sequential Scan Case
&lt;/h2&gt;

&lt;p&gt;If PostgreSQL performs a &lt;code&gt;Seq Scan&lt;/code&gt;, pgAssistant evaluates whether an index could realistically improve performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Indexed Access Case
&lt;/h2&gt;

&lt;p&gt;If PostgreSQL already uses an index, pgAssistant does not stop there.&lt;/p&gt;

&lt;p&gt;It also analyzes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Index Cond&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Filter&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Recheck Cond&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Rows Removed by Filter&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allows pgAssistant to detect situations such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Index Scan using idx_customer on orders
  Index Cond: (customer_id = 42)
  Filter: (employee_id = 5)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, PostgreSQL uses an index, but still visits many rows that are later discarded by the executor.&lt;/p&gt;

&lt;p&gt;pgAssistant can therefore recommend a more selective composite index such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead of considering the existing index “good enough”.&lt;/p&gt;




&lt;h1&gt;
  
  
  Predicate Classification
&lt;/h1&gt;

&lt;p&gt;Once predicates are extracted from the execution plan, pgAssistant classifies them by operator type.&lt;/p&gt;

&lt;p&gt;Internally, predicates are ranked according to B-Tree efficiency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Equality Predicates
&lt;/h2&gt;

&lt;p&gt;Highest priority:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="k"&gt;IN&lt;/span&gt;
&lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These predicates allow PostgreSQL to navigate directly to a very small portion of the index tree.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prefix Search Predicates
&lt;/h2&gt;

&lt;p&gt;Second priority:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'abc%'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prefix searches can still benefit efficiently from B-Tree traversal.&lt;/p&gt;




&lt;h2&gt;
  
  
  Range Predicates
&lt;/h2&gt;

&lt;p&gt;Lowest priority:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;
&lt;span class="k"&gt;BETWEEN&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once PostgreSQL enters a range scan, subsequent columns become far less effective for index navigation.&lt;/p&gt;

&lt;p&gt;That is why range predicates are typically placed last in composite indexes.&lt;/p&gt;




&lt;h1&gt;
  
  
  How pgAssistant Orders Index Columns
&lt;/h1&gt;

&lt;p&gt;After classifying predicates, pgAssistant computes candidate index ordering.&lt;/p&gt;

&lt;p&gt;The internal ordering logic is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Equality predicates first
2. Prefix predicates second
3. Range predicates last
4. Inside each category:
   highest cardinality first
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This logic is implemented directly inside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;reorder_index_candidate_columns&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The advisor therefore builds indexes that align with PostgreSQL B-Tree traversal behavior.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Column Cardinality Matters
&lt;/h1&gt;

&lt;p&gt;pgAssistant uses PostgreSQL statistics to estimate selectivity.&lt;/p&gt;

&lt;p&gt;The most important metric is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;n_distinct
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which estimates the number of distinct values in a column.&lt;/p&gt;

&lt;p&gt;Higher cardinality usually means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fewer matching rows&lt;/li&gt;
&lt;li&gt;better filtering&lt;/li&gt;
&lt;li&gt;smaller index scan ranges&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For our example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;customer_id [=]: n_distinct=89
employee_id [=]: n_distinct=9
order_date [&amp;gt;=]: n_distinct=814
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although &lt;code&gt;order_date&lt;/code&gt; has the highest cardinality, it is a range predicate and therefore placed last.&lt;/p&gt;

&lt;p&gt;Among equality predicates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;customer_id &amp;gt; employee_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;because:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;89 &amp;gt; 9
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final ordering becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  pgAssistant Uses PostgreSQL Statistics
&lt;/h1&gt;

&lt;p&gt;pgAssistant enriches every recommendation using planner statistics extracted from PostgreSQL.&lt;/p&gt;

&lt;p&gt;Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;n_distinct&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;null_frac&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;most_common_vals&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;most_common_freqs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;histogram_bounds&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The advisor even exposes these statistics in its recommendation reasoning.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;customer_id [=]: n_distinct=89, null_frac=0.0000
employee_id [=]: n_distinct=9, null_frac=0.0000
order_date [&amp;gt;=]: n_distinct=814
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes the recommendation transparent and explainable.&lt;/p&gt;




&lt;h1&gt;
  
  
  pgAssistant Also Uses Table Statistics
&lt;/h1&gt;

&lt;p&gt;An index is not always beneficial.&lt;/p&gt;

&lt;p&gt;This is one of the most important concepts in PostgreSQL optimization.&lt;/p&gt;

&lt;p&gt;For small tables, PostgreSQL often correctly prefers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Seq Scan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Index Scan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sequential reads are cheap&lt;/li&gt;
&lt;li&gt;index traversal has overhead&lt;/li&gt;
&lt;li&gt;heap fetches introduce random I/O&lt;/li&gt;
&lt;li&gt;scanning the entire table may cost less&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why pgAssistant also evaluates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;estimated row counts&lt;/li&gt;
&lt;li&gt;table size&lt;/li&gt;
&lt;li&gt;planner costs&lt;/li&gt;
&lt;li&gt;execution frequency&lt;/li&gt;
&lt;li&gt;workload intensity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The advisor does not blindly recommend indexes whenever a sequential scan appears.&lt;/p&gt;




&lt;h1&gt;
  
  
  Detecting Inefficient Indexed Access
&lt;/h1&gt;

&lt;p&gt;One particularly powerful aspect of pgAssistant is its ability to analyze residual filtering.&lt;/p&gt;

&lt;p&gt;For indexed scans, the advisor evaluates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rows Removed by Filter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If PostgreSQL retrieves many tuples from the index only to discard them afterward, pgAssistant detects that the current index may be incomplete.&lt;/p&gt;

&lt;p&gt;Internally, the advisor computes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Residual filter kept X% of tuples visited
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helps identify situations where adding an additional column to a composite index could drastically reduce heap filtering.&lt;/p&gt;




&lt;h1&gt;
  
  
  Detecting Planner Estimation Problems
&lt;/h1&gt;

&lt;p&gt;pgAssistant also compares:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plan_rows
vs
actual_rows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Large estimation gaps may indicate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stale statistics&lt;/li&gt;
&lt;li&gt;data skew&lt;/li&gt;
&lt;li&gt;correlation issues&lt;/li&gt;
&lt;li&gt;missing extended statistics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This additional analysis improves the reliability of recommendations.&lt;/p&gt;




&lt;h1&gt;
  
  
  The pgAssistant Recommendation Algorithm
&lt;/h1&gt;

&lt;p&gt;Conceptually, pgAssistant follows this workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Analyze EXPLAIN ANALYZE JSON plan
2. Detect access paths
3. Extract predicates from:
   - Index Cond
   - Filter
   - Recheck Cond
4. Classify predicates by operator type
5. Rank predicates for B-Tree efficiency
6. Use PostgreSQL statistics to estimate selectivity
7. Order columns by:
   - predicate class
   - cardinality
8. Evaluate table statistics
9. Evaluate execution costs
10. Detect residual filtering
11. Compare against existing indexes
12. Recommend index only if beneficial
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Example: Final Recommendation
&lt;/h1&gt;

&lt;p&gt;Given:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ship_country&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;employee_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;order_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;pgAssistant evaluates:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Column&lt;/th&gt;
&lt;th&gt;Predicate&lt;/th&gt;
&lt;th&gt;Priority&lt;/th&gt;
&lt;th&gt;n_distinct&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;customer_id&lt;/td&gt;
&lt;td&gt;&lt;code&gt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Equality&lt;/td&gt;
&lt;td&gt;89&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;employee_id&lt;/td&gt;
&lt;td&gt;&lt;code&gt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Equality&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;order_date&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Range&lt;/td&gt;
&lt;td&gt;814&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The advisor therefore generates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;CONCURRENTLY&lt;/span&gt;
    &lt;span class="nv"&gt;"pga_idx_orders_customer_id_employee_id_order_date"&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="nv"&gt;"public"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"orders"&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"customer_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"employee_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"order_date"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ordering follows PostgreSQL B-Tree optimization principles while also considering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;planner statistics&lt;/li&gt;
&lt;li&gt;table characteristics&lt;/li&gt;
&lt;li&gt;execution plan behavior&lt;/li&gt;
&lt;li&gt;residual filtering&lt;/li&gt;
&lt;li&gt;existing indexes already in use&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Live demo&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A public demo is available here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ov-004f8b.infomaniak.ch/" rel="noopener noreferrer"&gt;https://ov-004f8b.infomaniak.ch/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Demo connection:&lt;/p&gt;

&lt;p&gt;postgresql://postgres:demo@demo-db:5432/northwind&lt;/p&gt;

&lt;p&gt;The public demo intentionally runs without AI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project links&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/beh74/pgassistant-community" rel="noopener noreferrer"&gt;https://github.com/beh74/pgassistant-community&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Documentation: &lt;a href="https://beh74.github.io/pgassistant-blog/" rel="noopener noreferrer"&gt;https://beh74.github.io/pgassistant-blog/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docker image: &lt;a href="https://hub.docker.com/r/bertrand73/pgassistant" rel="noopener noreferrer"&gt;https://hub.docker.com/r/bertrand73/pgassistant&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Feedback welcome&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The project is still evolving and many parts can certainly be improved.&lt;/p&gt;

&lt;p&gt;If you work with PostgreSQL and have ideas, feedback, or criticisms, feel free to open an issue or discussion on GitHub.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>performance</category>
      <category>postgressql</category>
    </item>
    <item>
      <title>pgAssistant 2.8 — Deterministic PostgreSQL Analysis with the new Global Advisor</title>
      <dc:creator>bertrand HARTWIG</dc:creator>
      <pubDate>Fri, 08 May 2026 06:01:15 +0000</pubDate>
      <link>https://dev.to/bertrand_hartwig_309d1958/pgassistant-28-deterministic-postgresql-analysis-with-the-new-global-advisor-ig4</link>
      <guid>https://dev.to/bertrand_hartwig_309d1958/pgassistant-28-deterministic-postgresql-analysis-with-the-new-global-advisor-ig4</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8vzi2hozpxzikvsrko15.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8vzi2hozpxzikvsrko15.png" alt="Global Advisor" width="800" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the past months, I have been working on a simple idea around PostgreSQL tooling:&lt;/p&gt;

&lt;p&gt;before using AI, start with deterministic analysis.&lt;/p&gt;

&lt;p&gt;This is the direction behind &lt;strong&gt;pgAssistant 2.8&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This release introduces a new component called Global Advisor, alongside many improvements around ranking, schema analysis, maintenance diagnostics, and index recommendations.&lt;/p&gt;

&lt;p&gt;The project remains open-source and focused on practical PostgreSQL analysis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is pgAssistant?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;pgAssistant is an open-source PostgreSQL analysis tool.&lt;/p&gt;

&lt;p&gt;It helps developers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;inspect database structures&lt;/li&gt;
&lt;li&gt;analyze execution plans&lt;/li&gt;
&lt;li&gt;detect schema and maintenance issues&lt;/li&gt;
&lt;li&gt;review indexes and foreign keys&lt;/li&gt;
&lt;li&gt;understand PostgreSQL behavior more easily&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The project combines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;deterministic analysis&lt;/li&gt;
&lt;li&gt;execution-plan analysis (EXPLAIN ANALYZE)&lt;/li&gt;
&lt;li&gt;optional AI-assisted reasoning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not to replace PostgreSQL expertise.&lt;br&gt;
The goal is simply to make PostgreSQL diagnostics more accessible and more contextual.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The main addition in 2.8: Global Advisor&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before pgAssistant 2.8, most checks existed independently.&lt;/p&gt;

&lt;p&gt;Now they are consolidated into a single entry point:&lt;/p&gt;

&lt;p&gt;Global Advisor&lt;/p&gt;

&lt;p&gt;The Global Advisor performs a database-wide deterministic analysis and aggregates findings into a unified recommendation list.&lt;/p&gt;

&lt;p&gt;Each recommendation now includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a rank&lt;/li&gt;
&lt;li&gt;a confidence score&lt;/li&gt;
&lt;li&gt;an estimated impact&lt;/li&gt;
&lt;li&gt;an estimated implementation effort&lt;/li&gt;
&lt;li&gt;a suggested SQL statement when relevant&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5wtwlmm1kr3jvkxv78jr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5wtwlmm1kr3jvkxv78jr.png" alt="Global Advisor" width="800" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The objective is not to claim certainty.&lt;/p&gt;

&lt;p&gt;It is to help prioritize investigations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deterministic first&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One important design choice in pgAssistant is that the Global Advisor is intentionally deterministic.&lt;/p&gt;

&lt;p&gt;The analysis is based directly on PostgreSQL catalogs and statistics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pg_stat_user_tables&lt;/li&gt;
&lt;li&gt;pg_stat_user_indexes&lt;/li&gt;
&lt;li&gt;pg_constraint&lt;/li&gt;
&lt;li&gt;pg_index&lt;/li&gt;
&lt;li&gt;pg_settings&lt;/li&gt;
&lt;li&gt;pg_stats&lt;/li&gt;
&lt;li&gt;execution plans&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;same input → same output&lt;/li&gt;
&lt;li&gt;no hallucinations&lt;/li&gt;
&lt;li&gt;explainable findings&lt;/li&gt;
&lt;li&gt;reproducible analysis&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI is still supported as an optional layer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzlg8zpf1fk1y2iap2ggw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzlg8zpf1fk1y2iap2ggw.png" alt="Query Advisor" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Examples of checks now included&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Global Advisor currently includes checks such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;missing indexes on foreign keys&lt;/li&gt;
&lt;li&gt;redundant or duplicate indexes&lt;/li&gt;
&lt;li&gt;unused indexes&lt;/li&gt;
&lt;li&gt;invalid indexes&lt;/li&gt;
&lt;li&gt;datatype inconsistencies on foreign keys&lt;/li&gt;
&lt;li&gt;tables without primary keys&lt;/li&gt;
&lt;li&gt;stale statistics&lt;/li&gt;
&lt;li&gt;tables never vacuumed&lt;/li&gt;
&lt;li&gt;estimated table bloat&lt;/li&gt;
&lt;li&gt;excessive index-to-table ratio&lt;/li&gt;
&lt;li&gt;low foreign key coverage&lt;/li&gt;
&lt;li&gt;PostgreSQL configuration checks&lt;/li&gt;
&lt;li&gt;sequences approaching exhaustion&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most recommendations also include suggested SQL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Query analysis is still there&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The query advisor based on real EXPLAIN ANALYZE plans remains a core part of pgAssistant.&lt;/p&gt;

&lt;p&gt;The idea is now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Global Advisor → broad database analysis&lt;/li&gt;
&lt;li&gt;Query Advisor → detailed query-level investigation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These two approaches complement each other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;About AI&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI support remains optional.&lt;/p&gt;

&lt;p&gt;pgAssistant can work entirely without an LLM.&lt;/p&gt;

&lt;p&gt;When enabled, AI features receive contextual PostgreSQL information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;schema definitions&lt;/li&gt;
&lt;li&gt;indexes&lt;/li&gt;
&lt;li&gt;execution plans&lt;/li&gt;
&lt;li&gt;statistics&lt;/li&gt;
&lt;li&gt;database settings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This significantly improves the relevance of generated suggestions compared to generic SQL prompting.&lt;/p&gt;

&lt;p&gt;Supported providers currently include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ollama&lt;/li&gt;
&lt;li&gt;OpenAI-compatible APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why I built it this way&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A lot of PostgreSQL tooling focuses on metrics dashboards.&lt;/p&gt;

&lt;p&gt;Those tools are useful, but I often felt there was still a gap between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;seeing a metric&lt;/li&gt;
&lt;li&gt;understanding the cause&lt;/li&gt;
&lt;li&gt;deciding what to change&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;pgAssistant tries to reduce that gap.&lt;/p&gt;

&lt;p&gt;The project is still evolving, but the Global Advisor is an important step toward a more coherent analysis workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live demo&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A public demo is available here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ov-004f8b.infomaniak.ch/" rel="noopener noreferrer"&gt;https://ov-004f8b.infomaniak.ch/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Demo connection:&lt;/p&gt;

&lt;p&gt;postgresql://postgres:demo@demo-db:5432/northwind&lt;/p&gt;

&lt;p&gt;The public demo intentionally runs without AI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project links&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/beh74/pgassistant-community" rel="noopener noreferrer"&gt;https://github.com/beh74/pgassistant-community&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Documentation: &lt;a href="https://beh74.github.io/pgassistant-blog/" rel="noopener noreferrer"&gt;https://beh74.github.io/pgassistant-blog/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docker image: &lt;a href="https://hub.docker.com/r/bertrand73/pgassistant" rel="noopener noreferrer"&gt;https://hub.docker.com/r/bertrand73/pgassistant&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Feedback welcome&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The project is still evolving and many parts can certainly be improved.&lt;/p&gt;

&lt;p&gt;If you work with PostgreSQL and have ideas, feedback, or criticisms, feel free to open an issue or discussion on GitHub.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>database</category>
      <category>opensource</category>
      <category>postgres</category>
      <category>tooling</category>
    </item>
    <item>
      <title>pgAssistant v1.7 released</title>
      <dc:creator>bertrand HARTWIG</dc:creator>
      <pubDate>Sat, 01 Feb 2025 13:09:19 +0000</pubDate>
      <link>https://dev.to/bertrand_hartwig_309d1958/pgassistant-v17-released-3309</link>
      <guid>https://dev.to/bertrand_hartwig_309d1958/pgassistant-v17-released-3309</guid>
      <description>&lt;p&gt;I'm excited to share that we just released &lt;strong&gt;pgAssistant&lt;/strong&gt; v1.7.&lt;/p&gt;

&lt;p&gt;PGAssistant is an open-source tool designed to help &lt;strong&gt;developers&lt;/strong&gt; gain deeper insights into their PostgreSQL databases and optimize performance efficiently.&lt;/p&gt;

&lt;p&gt;It analyzes database behavior, detects schema-related issues, and provides actionable recommendations to resolve them.&lt;/p&gt;

&lt;p&gt;One of the goals of PGAssistant is to help developers optimize their database and fix potential issues on their own before needing to seek assistance from a DBA.&lt;/p&gt;

&lt;p&gt;🚀 &lt;strong&gt;AI-Powered Optimization:&lt;/strong&gt; PGAssistant leverages AI-driven language models like ChatGPT, Claude, and on-premise solutions such as Ollama to assist developers in refining complex queries and enhancing database efficiency.&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/nexsol-technologies/pgassistant" rel="noopener noreferrer"&gt;PGAssistant&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🚀 &lt;strong&gt;Easy Deployment with Docker:&lt;/strong&gt; PGAssistant is Docker-based, making it simple to run. Get started effortlessly using the provided &lt;a href="https://github.com/nexsol-technologies/pgassistant/blob/main/docker-compose/docker-compose.yml" rel="noopener noreferrer"&gt;Docker Compose file&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’d love to hear your feedback! If you find PGAssistant useful, feel free to contribute or suggest new features. Let’s make PostgreSQL database easy for dev Teams !&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>postgressql</category>
      <category>productivity</category>
      <category>ai</category>
    </item>
    <item>
      <title>pgAssistant - postgesql tool 4 dev</title>
      <dc:creator>bertrand HARTWIG</dc:creator>
      <pubDate>Mon, 12 Aug 2024 05:00:03 +0000</pubDate>
      <link>https://dev.to/bertrand_hartwig_309d1958/pgassistant-postgesql-tool-4-dev-16cp</link>
      <guid>https://dev.to/bertrand_hartwig_309d1958/pgassistant-postgesql-tool-4-dev-16cp</guid>
      <description>&lt;p&gt;I wrote this small tool because I noticed that our young developers were struggling to understand the behavior of their PostgreSQL database, and therefore had even more difficulty optimizing their databases. &lt;/p&gt;

&lt;p&gt;Gradually, developers started using it, and the tool helped the development teams become much more autonomous, and me much less solicited. &lt;/p&gt;

&lt;p&gt;Feel free to use it and give me your feedback. &lt;/p&gt;

&lt;p&gt;I try to enhance the application whenever time allows.&lt;/p&gt;

&lt;p&gt;You can find it there : &lt;a href="https://github.com/nexsol-technologies/pgassistant" rel="noopener noreferrer"&gt;https://github.com/nexsol-technologies/pgassistant&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>postgres</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
