{"id":2495,"date":"2026-07-29T17:36:56","date_gmt":"2026-07-29T17:36:56","guid":{"rendered":"https:\/\/www.tiagoneves.net\/blog\/?p=2495"},"modified":"2026-07-29T17:36:56","modified_gmt":"2026-07-29T17:36:56","slug":"estatisticas-no-postgresql-o-que-o-planner-enxerga","status":"publish","type":"post","link":"https:\/\/www.tiagoneves.net\/blog\/estatisticas-no-postgresql-o-que-o-planner-enxerga\/","title":{"rendered":"Estat\u00edsticas no PostgreSQL: o que o planner enxerga"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>Post 02 da s\u00e9rie &#8220;Estat\u00edsticas: SQL Server \u00d7 PostgreSQL&#8221;<\/strong> <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">01 \u00b7 <a href=\"https:\/\/www.tiagoneves.net\/blog\/estatisticas-no-sql-server-por-que-seus-planos-de-execucao-comecam-aqui\/\">Estat\u00edsticas no SQL Server<\/a> <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u2705 \u00b7 <strong>02 \u00b7 PostgreSQL (voc\u00ea est\u00e1 aqui)<\/strong> <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">03 \u00b7 Comparativo (em breve)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ambiente usado nos exemplos: PostgreSQL 18, tabela de 5 milh\u00f5es de linhas gerada com <code>generate_series<\/code>. Todos os comandos est\u00e3o no final para voc\u00ea reproduzir.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-cyan-blue-color\">O sintoma<\/mark><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Voc\u00ea rodou <code>ANALYZE<\/code>. O plano continua ruim.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">No SQL Server, a gente sabe o caminho: abre um <code>DBCC SHOW_STATISTICS<\/code>, olha o histograma, o density vector, a data da \u00faltima atualiza\u00e7\u00e3o. No PostgreSQL, muita gente roda o <code>ANALYZE<\/code>, cruza os dedos&#8230; e para por a\u00ed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Neste post vamos tentar entender o comportamento das estatisticas no PostgreSQL, o que exatamente o <code>ANALYZE<\/code> coleta, onde isso fica guardado, como o planner usa cada pe\u00e7a e quem decide <strong>quando<\/strong> essa foto \u00e9 tirada (spoiler: n\u00e3o \u00e9 voc\u00ea, \u00e9 o autovacuum).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Como no Post 01, nada aqui \u00e9 teoria de manual todos os n\u00fameros vieram de um PostgreSQL 18 real rodando na minha m\u00e1quina.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">E a escolha da vers\u00e3o n\u00e3o foi por acaso: <strong>o PostgreSQL 18 mexeu justamente em estat\u00edsticas<\/strong>, em dois pontos que mudam a rotina de quem administra base grande:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\ud83d\udccc O que mudou no PostgreSQL 18 (e importa para este post)<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Estat\u00edsticas sobrevivem ao upgrade de vers\u00e3o maior.<\/strong> At\u00e9 o PG 17, o <code>pg_upgrade<\/code> descartava as estat\u00edsticas do planner o cluster subia &#8220;cego&#8221; e degradava at\u00e9 o <code>ANALYZE<\/code> geral terminar. No 18, elas s\u00e3o preservadas, e a janela de risco p\u00f3s-upgrade praticamente desaparece.<br><\/li>\n\n\n\n<li><strong><code>ANALYZE<\/code> em tabela particionada ficou recursivo por padr\u00e3o.<\/strong> Rodar na tabela-m\u00e3e agora processa tamb\u00e9m as parti\u00e7\u00f5es filhas; a nova palavra <code>ONLY<\/code> restringe \u00e0 estrutura-m\u00e3e. Quem cuida de VLDB particionado precisa revisar os scripts de manuten\u00e7\u00e3o antes de migrar.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\"><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-cyan-blue-color\">O planner cego: o que acontece sem estat\u00edstica nenhuma<\/mark><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Criei uma tabela <code>vendas<\/code> com 5 milh\u00f5es de linhas e uma distribui\u00e7\u00e3o propositalmente desbalanceada o tipo de dado que a gente encontra na vida real:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>5 clientes &#8220;VIP&#8221;<\/strong> concentram 40% das vendas (~400 mil cada);<\/li>\n\n\n\n<li><strong>~10 mil clientes<\/strong> dividem os outros 60% (~300 vendas cada).<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Com o autovacuum temporariamente desligado na tabela (s\u00f3 para a demo!) e sem <code>ANALYZE<\/code>, perguntei ao planner sobre dois clientes de realidades opostas:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>DROP TABLE IF EXISTS vendas;\n\nCREATE TABLE vendas (\n    id          bigint GENERATED ALWAYS AS IDENTITY,\n    cliente_id  integer      NOT NULL,\n    data_venda  date         NOT NULL,\n    valor       numeric(10,2) NOT NULL\n) WITH (autovacuum_enabled = false);\n\nINSERT INTO vendas (cliente_id, data_venda, valor)\nSELECT\n    CASE WHEN g % 10 &lt; 4\n         THEN (g % 5) + 1                       -- 40% \u2192 clientes 1..5\n         ELSE 6 + (g % 9995)                    -- 60% \u2192 clientes 6..10000\n    END,\n    DATE '2024-01-01' + (g \/ 6850),             -- ~6.850 vendas\/dia, sequencial\n    round((random() * 1000)::numeric, 2)\nFROM generate_series(1, 5000000) AS g;\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>EXPLAIN \nSELECT * FROM vendas WHERE cliente_id = 3;     -- VIP: ~400.000 linhas\n<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-14.png?ssl=1\" rel=\"lightbox[2495]\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"678\" height=\"324\" src=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-14.png?resize=678%2C324&#038;ssl=1\" alt=\"\" class=\"wp-image-2496\" srcset=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-14.png?w=699&amp;ssl=1 699w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-14.png?resize=300%2C143&amp;ssl=1 300w\" sizes=\"auto, (max-width: 678px) 100vw, 678px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Veja que o Planer estimou o retorno de 21657 linhas, mas a consulta retornou 500000.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-15.png?ssl=1\" rel=\"lightbox[2495]\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"412\" height=\"354\" src=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-15.png?resize=412%2C354&#038;ssl=1\" alt=\"\" class=\"wp-image-2497\" srcset=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-15.png?w=412&amp;ssl=1 412w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-15.png?resize=300%2C258&amp;ssl=1 300w\" sizes=\"auto, (max-width: 412px) 100vw, 412px\" \/><\/a><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>EXPLAIN \nSELECT * FROM vendas WHERE cliente_id = 7777;<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-16.png?ssl=1\" rel=\"lightbox[2495]\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"489\" height=\"155\" src=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-16.png?resize=489%2C155&#038;ssl=1\" alt=\"\" class=\"wp-image-2498\" srcset=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-16.png?w=489&amp;ssl=1 489w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-16.png?resize=300%2C95&amp;ssl=1 300w\" sizes=\"auto, (max-width: 489px) 100vw, 489px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Com o <code>cliente_id = 7777<\/code> o Planner estimou as mesmas 21657 linhas, mas a consulta retornou 250.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-17.png?ssl=1\" rel=\"lightbox[2495]\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"405\" height=\"388\" src=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-17.png?resize=405%2C388&#038;ssl=1\" alt=\"\" class=\"wp-image-2499\" srcset=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-17.png?w=405&amp;ssl=1 405w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-17.png?resize=300%2C287&amp;ssl=1 300w\" sizes=\"auto, (max-width: 405px) 100vw, 405px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">As duas consultas receberam <strong>a mesma estimativa<\/strong>. Sem estat\u00edsticas, o planner n\u00e3o tem como saber que o cliente 3 \u00e9 um gigante e o 7777 \u00e9 raro ele aplica uma seletividade padr\u00e3o e torce para estar certo. Detalhe pequeno (como diria o rei da sexta-feira), o <code>pg_class.reltuples<\/code> fica em <strong>-1<\/strong> numa tabela nunca analisada, sinalizando &#8220;n\u00e3o fa\u00e7o ideia, vou estimar pelo tamanho f\u00edsico&#8221;.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">Traduzindo para SQL Server\u00eas:<\/mark><\/strong> \u00e9 o equivalente a uma tabela sem estat\u00edsticas e com AUTO_CREATE_STATISTICS desligado o otimizador chuta com guessed selectivity. A diferen\u00e7a \u00e9 que no SQL Server o primeiro SELECT com predicado j\u00e1 cria a estat\u00edstica automaticamente, no PostgreSQL, <strong>consulta nenhuma cria estat\u00edstica<\/strong> s\u00f3 o <code>ANALYZE<\/code> (manual ou via autovacuum).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-cyan-blue-color\">ANALYZE e o n\u00famero m\u00e1gico de 30.000 linhas<\/mark><\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>ANALYZE (VERBOSE) vendas;<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-18.png?ssl=1\" rel=\"lightbox[2495]\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"678\" height=\"119\" src=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-18.png?resize=678%2C119&#038;ssl=1\" alt=\"\" class=\"wp-image-2500\" srcset=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-18.png?resize=1024%2C179&amp;ssl=1 1024w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-18.png?resize=300%2C53&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-18.png?resize=768%2C134&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-18.png?w=1080&amp;ssl=1 1080w\" sizes=\"auto, (max-width: 678px) 100vw, 678px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Olha o que a mensagem entrega: numa tabela de <strong>5 milh\u00f5es<\/strong> de linhas, o <code>ANALYZE<\/code> leu uma amostra de <strong>30.000<\/strong>. De onde vem esse n\u00famero?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Amostra = 300 \u00d7 statistics_target.<\/strong> O <code>default_statistics_target<\/code> padr\u00e3o \u00e9 100, logo 300 \u00d7 100 = 30.000 linhas n\u00e3o importa se a tabela tem 1 milh\u00e3o ou 4 bilh\u00f5es de linhas. O fator 300 n\u00e3o \u00e9 chute o coment\u00e1rio no c\u00f3digo-fonte (<code>src\/backend\/commands\/analyze.c<\/code>) referencia um paper de 1998 sobre amostragem aleat\u00f3ria para constru\u00e7\u00e3o de histogramas, que deriva o limite estat\u00edstico m\u00ednimo (305, arredondado para 300).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Trinta mil linhas decidindo o destino de 5 milh\u00f5es \u00e9 pouco? para a maioria dos casos, n\u00e3o e \u00e9 exatamente por isso que o <code>ANALYZE<\/code> \u00e9 r\u00e1pido mesmo em VLDBs. Mas guarde essa propor\u00e7\u00e3o ela explica muita estimativa torta em coluna com distribui\u00e7\u00e3o maluca.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">Traduzindo para SQL Server\u00eas:<\/mark><\/strong> o papel do <code>ANALYZE<\/code> \u00e9 o do <code>UPDATE STATISTICS ... WITH SAMPLE<\/code>. A diferen\u00e7a filos\u00f3fica o SQL Server calcula a taxa de amostragem em fun\u00e7\u00e3o do tamanho da tabela, o Postgres usa amostra de tamanho (quase) fixo definida pelo target. E n\u00e3o existe <code>FULLSCAN<\/code> no ANALYZE o m\u00e1ximo que voc\u00ea consegue \u00e9 subir o target (limite 10.000 \u2192 amostra de 3 milh\u00f5es de linhas).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-cyan-blue-color\">Dentro do pg_stats: as quatro pe\u00e7as que decidem tudo<\/mark><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">O <code>ANALYZE<\/code> grava tudo no cat\u00e1logo <code>pg_statistic<\/code> (formato interno, ileg\u00edvel para humanos normais). A view <strong><code>pg_stats<\/code><\/strong> \u00e9 a vers\u00e3o para gente como a gente:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT n_distinct, correlation,\n       most_common_vals, most_common_freqs, histogram_bounds\nFROM pg_stats\nWHERE tablename = 'vendas' AND attname = 'cliente_id';<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-19.png?ssl=1\" rel=\"lightbox[2495]\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"678\" height=\"97\" src=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-19.png?resize=678%2C97&#038;ssl=1\" alt=\"\" class=\"wp-image-2501\" srcset=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-19.png?resize=1024%2C147&amp;ssl=1 1024w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-19.png?resize=300%2C43&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-19.png?resize=768%2C110&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-19.png?w=1073&amp;ssl=1 1073w\" sizes=\"auto, (max-width: 678px) 100vw, 678px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>As quatro pe\u00e7as principais:<\/strong><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">most_common_vals + most_common_freqs (MCV)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A lista dos &#8220;famosos&#8221; os valores mais frequentes da coluna e a fra\u00e7\u00e3o exata de linhas de cada um. Na demo os clientes 1 a 5 apareceram no topo com frequ\u00eancia \u2248 0,08 (8%) cada exatamente o skew que eu plantei na carga. Quando o predicado bate num MCV, o planner nem estima ele <strong>l\u00ea a frequ\u00eancia direto da lista<\/strong>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">histogram_bounds<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Onde vivem os &#8220;n\u00e3o famosos&#8221;? depois de separar os MCVs o restante dos valores \u00e9 distribu\u00eddo num histograma de at\u00e9 <code>statistics_target<\/code> buckets <strong>equialtos<\/strong> cada bucket cont\u00e9m aproximadamente a mesma quantidade de linhas. Com target 100, cada bucket responde por ~1% das linhas n\u00e3o-MCV.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">n_distinct<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A cardinalidade estimada da coluna. Pegadinha cl\u00e1ssica de leitura valor <strong>positivo<\/strong> \u00e9 contagem absoluta, valor <strong>negativo<\/strong> \u00e9 fra\u00e7\u00e3o das linhas (<code>-1<\/code> = todas distintas, <code>-0.5<\/code> = 50% distintas). Colunas tipo PK aparecem como <code>-1<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">correlation<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">De -1 a 1 o quanto a ordem f\u00edsica das linhas no disco acompanha a ordem l\u00f3gica do valor. Na demo, <code>data_venda<\/code> deu correlation \u2248 1 (inseri em ordem cronol\u00f3gica) e isso muda decis\u00e3o de plano correla\u00e7\u00e3o alta barateia Index Scan em faixas, correla\u00e7\u00e3o baixa empurra o planner para Bitmap Heap Scan.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">Traduzindo para SQL Server\u00eas:<\/mark><\/strong><\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>SQL Server<\/th><th>PostgreSQL<\/th><th>Observa\u00e7\u00e3o<\/th><\/tr><\/thead><tbody><tr><td><code>DBCC SHOW_STATISTICS<\/code><\/td><td><code>SELECT ... FROM pg_stats<\/code><\/td><td>No PG \u00e9 uma view: d\u00e1 pra filtrar, ordenar, cruzar.<\/td><\/tr><tr><td>Histograma (m\u00e1x. 200 steps)<\/td><td><code>histogram_bounds<\/code> (target buckets, padr\u00e3o 100)<\/td><td>PG separa os frequentes ANTES (MCV); SQL Server embute no step (<code>EQ_ROWS<\/code>).<\/td><\/tr><tr><td><code>EQ_ROWS<\/code> dos steps<\/td><td><code>most_common_vals\/freqs<\/code><\/td><td>O MCV \u00e9 mais expl\u00edcito e pode ter at\u00e9 10.000 entradas.<\/td><\/tr><tr><td>Density vector<\/td><td><code>n_distinct<\/code><\/td><td>Cuidado com a sem\u00e2ntica do valor negativo.<\/td><\/tr><tr><td><em>(sem equivalente direto)<\/em><\/td><td><code>correlation<\/code><\/td><td>O SQL Server n\u00e3o exp\u00f5e isso na estat\u00edstica.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Uma invers\u00e3o honesta em visibilidade de cat\u00e1logo, o <code>pg_stats<\/code> \u00e9 <strong>mais confort\u00e1vel<\/strong> que o <code>DBCC SHOW_STATISTICS<\/code>. J\u00e1 em rastreabilidade de uso (qual estat\u00edstica o plano usou), o SQL Server ganha.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-cyan-blue-color\">O Planner com olhos mesma consulta, planos diferentes<\/mark><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Depois do <code>ANALYZE<\/code>, repeti as duas consultas do in\u00edcio:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>EXPLAIN \n(ANALYZE, TIMING OFF)\nSELECT * FROM vendas WHERE cliente_id = 3;<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-20.png?ssl=1\" rel=\"lightbox[2495]\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"626\" height=\"239\" src=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-20.png?resize=626%2C239&#038;ssl=1\" alt=\"\" class=\"wp-image-2502\" srcset=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-20.png?w=626&amp;ssl=1 626w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-20.png?resize=300%2C115&amp;ssl=1 300w\" sizes=\"auto, (max-width: 626px) 100vw, 626px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><code>EXPLAIN <br>(ANALYZE, TIMING OFF)<br>SELECT * FROM vendas WHERE cliente_id = 7777;<\/code><\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-21.png?ssl=1\" rel=\"lightbox[2495]\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"628\" height=\"297\" src=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-21.png?resize=628%2C297&#038;ssl=1\" alt=\"\" class=\"wp-image-2503\" srcset=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-21.png?w=628&amp;ssl=1 628w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-21.png?resize=300%2C142&amp;ssl=1 300w\" sizes=\"auto, (max-width: 628px) 100vw, 628px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Veja que agora sim o Planner estimou o rows mais proximo do valor real de linhas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u00c9 a frase que abriu o Post 01, agora do lado de c\u00e1: <strong>todo plano nasce de uma estimativa, e toda estimativa nasce da estat\u00edstica.<\/strong><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-cyan-blue-color\">Comprando precis\u00e3o: default_statistics_target<\/mark><\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">E quando 30 mil linhas n\u00e3o bastam? Voc\u00ea sobe o target de prefer\u00eancia <strong>por coluna<\/strong>, n\u00e3o globalmente:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ALTER TABLE vendas ALTER COLUMN cliente_id SET STATISTICS 1000;\nANALYZE (VERBOSE) vendas;<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-22.png?ssl=1\" rel=\"lightbox[2495]\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"678\" height=\"116\" src=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-22.png?resize=678%2C116&#038;ssl=1\" alt=\"\" class=\"wp-image-2504\" srcset=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-22.png?resize=1024%2C175&amp;ssl=1 1024w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-22.png?resize=300%2C51&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-22.png?resize=768%2C131&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-22.png?w=1082&amp;ssl=1 1082w\" sizes=\"auto, (max-width: 678px) 100vw, 678px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Target 1000 \u2192 amostra de 300 mil linhas, MCV com at\u00e9 1000 entradas, histograma com at\u00e9 1000 buckets, <code>n_distinct<\/code> mais preciso. O custo: <code>ANALYZE<\/code> mais lento (em toda execu\u00e7\u00e3o, inclusive as do autovacuum, para sempre) e listas maiores que o Planner varre a cada plano. Estat\u00edstica n\u00e3o \u00e9 de gra\u00e7a em nenhuma das pontas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">Traduzindo para SQL Server\u00eas:<\/mark><\/strong> \u00e9 o bot\u00e3o que a gente n\u00e3o tem. No SQL Server, o m\u00e1ximo de steps \u00e9 200 e ponto; no PG, o histograma escala at\u00e9 10.000 buckets se voc\u00ea pagar o pre\u00e7o. Em contrapartida, o <code>WITH FULLSCAN<\/code> do SQL Server n\u00e3o tem equivalente.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-cyan-blue-color\">Quem decide quando a estat\u00edstica \u00e9 atualizada: o autovacuum<\/mark><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Aqui mora a diferen\u00e7a cultural mais importante entre os dois mundos e ela merece ser explicada do zero.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Primeiro: quem \u00e9 o autovacuum?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">No SQL Server, a atualiza\u00e7\u00e3o autom\u00e1tica de estat\u00edsticas acontece <strong>dentro da sua consulta<\/strong> o otimizador percebe que a estat\u00edstica est\u00e1 velha e dispara o update na hora (sincrono ou ass\u00edncrono, conforme a configura\u00e7\u00e3o).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">No PostgreSQL n\u00e3o existe nada disso embutido na consulta, quem cuida da manuten\u00e7\u00e3o \u00e9 um <strong>processo separado que roda em segundo plano<\/strong> chamado <strong>autovacuum<\/strong> pense nele como um &#8220;SQL Agent de f\u00e1brica&#8221; que j\u00e1 vem ligado e tem duas miss\u00f5es:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>VACUUM<\/strong> \u2192 limpar linhas mortas (assunto para outro post);<\/li>\n\n\n\n<li><strong>ANALYZE autom\u00e1tico<\/strong> \u2192 atualizar as estat\u00edsticas (o nosso assunto).<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">De tempos em tempos (a cada <code>autovacuum_naptime<\/code>, padr\u00e3o <strong>1 minuto<\/strong>), \u00e9 executado em background, olha tabela por tabela e pergunta: <em>&#8220;essa aqui mudou o suficiente para merecer um ANALYZE novo?&#8221;<\/em><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SHOW autovacuum_analyze_threshold;      -- &#91;VALIDAR] 50 (padr\u00e3o)\nSHOW autovacuum_analyze_scale_factor;   -- &#91;VALIDAR] 0.1 (padr\u00e3o)\nSHOW autovacuum_naptime;                -- &#91;VALIDAR] 1min (padr\u00e3o)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Segundo: o que \u00e9 &#8220;mudou o suficiente&#8221;?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">O Postgresql mant\u00e9m para cada tabela, um contador de modifica\u00e7\u00f5es desde o \u00faltimo ANALYZE voc\u00ea pode v\u00ea-lo com seus pr\u00f3prios olhos:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT n_mod_since_analyze   -- INSERTs + UPDATEs + DELETEs acumulados\nFROM pg_stat_user_tables\nWHERE relname = 'vendas';<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">O autovacuum compara esse contador com um limite calculado assim:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">limite = 50  +  10% do total de linhas da tabela<br>         \u2514\u2500 autovacuum_analyze_threshold (padr\u00e3o)<br>                \u2514\u2500 autovacuum_analyze_scale_factor = 0.1 (padr\u00e3o)<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Se o contador passou do limite \u2192 ele roda o ANALYZE sozinho e zera o contador, se n\u00e3o passou \u2192 ele n\u00e3o faz nada e suas estat\u00edsticas continuam <strong>exatamente como estavam<\/strong>, por mais consultas que voc\u00ea rode.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Terceiro: a conta na nossa tabela<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Para a <code>vendas<\/code>, de 5 milh\u00f5es de linhas:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">limite = 50 + (0,1 \u00d7 5.000.000) = 500.050 modifica\u00e7\u00f5es<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u00c9 isso mesmo que voc\u00ea viu: <strong>meio milh\u00e3o de linhas podem ser inseridas, alteradas ou apagadas sem que o Postgresql atualize estat\u00edstica nenhuma.<\/strong> Se o seu skew mudou nesse meio tempo um cliente pequeno virou VIP, uma faixa nova de datas entrou o planner segue tomando decis\u00e3o com as estatisticas velha.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Quarto: vendo o gatilho disparar ao vivo<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Fa\u00e7a o teste ai tamb\u00e9m, atualizei ~800 mil linhas (acima do limite de 500.050) e fotografei o contador antes e depois do ciclo do autovacuum:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT n_mod_since_analyze, last_autoanalyze\nFROM pg_stat_user_tables WHERE relname = 'vendas';\n\n<code>UPDATE vendas SET valor = valor + 1 WHERE cliente_id &lt;= 2; -- ~100000 mil linhas<\/code><\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-25.png?ssl=1\" rel=\"lightbox[2495]\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"430\" height=\"117\" src=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-25.png?resize=430%2C117&#038;ssl=1\" alt=\"\" class=\"wp-image-2507\" srcset=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-25.png?w=430&amp;ssl=1 430w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-25.png?resize=300%2C82&amp;ssl=1 300w\" sizes=\"auto, (max-width: 430px) 100vw, 430px\" \/><\/a><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-27.png?ssl=1\" rel=\"lightbox[2495]\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"401\" height=\"103\" src=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-27.png?resize=401%2C103&#038;ssl=1\" alt=\"\" class=\"wp-image-2509\" srcset=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-27.png?w=401&amp;ssl=1 401w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-27.png?resize=300%2C77&amp;ssl=1 300w\" sizes=\"auto, (max-width: 401px) 100vw, 401px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">O <code>last_autoanalyze<\/code> preenchido \u00e9 a prova do crime: ningu\u00e9m rodou nada o autovacuum foi executado, fez a conta, viu que 1 milh\u00e3o > 500 mil e tirou a foto nova sozinho.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Quinto: por que isso \u00e9 um problema em tabela grande (e como ajustar)<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Os 10% s\u00e3o fixos, ent\u00e3o o limite <strong>cresce junto com a tabela<\/strong>:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Tamanho da tabela<\/th><th>Modifica\u00e7\u00f5es at\u00e9 o auto-analyze<\/th><\/tr><\/thead><tbody><tr><td>100 mil linhas<\/td><td>10.050<\/td><\/tr><tr><td>5 milh\u00f5es<\/td><td>500.050<\/td><\/tr><tr><td>1 bilh\u00e3o<\/td><td><strong>100.000.050<\/strong><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Numa tabela de 1 bilh\u00e3o de linhas, 100 milh\u00f5es de modifica\u00e7\u00f5es passam despercebidas. Por isso o ajuste cl\u00e1ssico em VLDB \u00e9 baixar o percentual <strong>por tabela<\/strong> (nunca globalmente, para n\u00e3o sobrecarregar o autovacuum nas pequenas):<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>-- Nesta tabela, reanalise a cada 1% de mudan\u00e7a em vez de 10%<br>ALTER TABLE vendas SET (autovacuum_analyze_scale_factor = 0.01);<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">Traduzindo para SQL Server\u00eas:<\/mark><\/strong> essa hist\u00f3ria voc\u00ea j\u00e1 viveu. O SQL Server usava threshold de <strong>20% + 500 linhas<\/strong> at\u00e9 o 2014 e sofria exatamente deste mal em tabela grande, tanto que existia a trace flag 2371 para ativar um threshold din\u00e2mico. A partir do 2016, o din\u00e2mico (\u221a(1000 \u00d7 linhas)) virou padr\u00e3o e o problema sumiu. O Postgresql ainda vive no &#8220;modelo de porcentagem fixa&#8221;: funciona bem em tabela pequena e m\u00e9dia, mas em VLDB a corre\u00e7\u00e3o \u00e9 manual, por tabela como era no seu SQL Server 2012. D\u00e9j\u00e0-vu completo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Outra diferen\u00e7a de mentalidade: no SQL Server, a estat\u00edstica velha \u00e9 detectada <strong>no momento da consulta<\/strong>, no Postgresql, entre um ciclo e outro do autovacuum, <strong>nenhuma consulta provoca atualiza\u00e7\u00e3o<\/strong> se voc\u00ea fez uma carga gigante e vai consultar em seguida, rode <code>ANALYZE<\/code> manualmente no fim da carga. \u00c9 o h\u00e1bito n\u00ba 1 que um DBA SQL Server precisa adquirir ao herdar um Postgresql.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-cyan-blue-color\">PostgreSQL 18 na pr\u00e1tica: particionadas e upgrades<\/mark><\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">As duas novidades que anunciei na introdu\u00e7\u00e3o merecem o mesmo tratamento em camadas porque as duas s\u00f3 fazem sentido quando voc\u00ea entende o que existia antes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Primeiro: como uma particionada guarda estat\u00edsticas no Postgresql<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">No PostgreSQL, cada parti\u00e7\u00e3o \u00e9 uma <strong>tabela de verdade<\/strong>, com vida pr\u00f3pria. Isso significa que uma particionada tem estat\u00edsticas em <strong>dois n\u00edveis<\/strong>:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Cada parti\u00e7\u00e3o filha<\/strong> tem seu pr\u00f3prio conjunto no <code>pg_statistic<\/code>, histograma, MCVs, tudo como qualquer tabela comum; <\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>A estrutura-m\u00e3e<\/strong> tem um conjunto SEPARADO de estat\u00edsticas &#8220;globais&#8221;, que enxerga o conjunto inteiro. \u00c9 esse conjunto que o planner usa quando a consulta cruza v\u00e1rias parti\u00e7\u00f5es (um JOIN por uma coluna que n\u00e3o \u00e9 a chave de particionamento, por exemplo).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">Traduzindo para SQL Server\u00eas:<\/mark><\/strong> repare que \u00e9 o desenho INVERTIDO do nosso. No SQL Server, a estat\u00edstica &#8220;oficial&#8221; \u00e9 uma s\u00f3, no n\u00edvel da tabela inteira e a gente sofre para ter granularidade por parti\u00e7\u00e3o (foi para isso que nasceram as estat\u00edsticas incrementais do 2014, tema do Post 01). No PostgreSQL, a granularidade por parti\u00e7\u00e3o vem de gra\u00e7a, porque cada parti\u00e7\u00e3o \u00e9 uma tabela e o trabalho extra fica em manter a vis\u00e3o do conjunto (a m\u00e3e).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Segundo: o que mudou no 18<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">At\u00e9 o PostgreSQL 17, rodar <code>ANALYZE<\/code> na tabela-m\u00e3e atualizava <strong>apenas as estat\u00edsticas globais da m\u00e3e<\/strong> ele amostrava as filhas para montar a vis\u00e3o do conjunto, mas <strong>n\u00e3o gravava<\/strong> as estat\u00edsticas individuais de cada filha. Quem quisesse tudo atualizado precisava rodar ANALYZE na m\u00e3e E em cada parti\u00e7\u00e3o.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">No 18, o comportamento virou o que todo mundo esperava intuitivamente <strong><code>ANALYZE<\/code> na m\u00e3e processa a m\u00e3e e todas as filhas<\/strong>, uma a uma. E nasceu a palavra <code>ONLY<\/code> para quem quer o comportamento antigo:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Antes vamos criar a estrutura da tabela particionada: (explicar a estrutura fica para um pr\u00f3ximo post)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>CREATE TABLE vendas_part (<br>id bigint, cliente_id int NOT NULL, data_venda date NOT NULL<br>) PARTITION BY RANGE (data_venda);<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>CREATE TABLE vendas_part_2024 PARTITION OF vendas_part<br>FOR VALUES FROM ('2024-01-01') TO ('2025-01-01');<br>CREATE TABLE vendas_part_2025 PARTITION OF vendas_part<br>FOR VALUES FROM ('2025-01-01') TO ('2026-01-01');<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>INSERT INTO vendas_part<br>SELECT g, g % 100, DATE '2024-01-01' + (g % 700)<br>FROM generate_series(1, 1000000) g;<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ANALYZE (VERBOSE) vendas_part;        -- PG 18: m\u00e3e + TODAS as filhas<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-29.png?ssl=1\" rel=\"lightbox[2495]\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"678\" height=\"260\" src=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-29.png?resize=678%2C260&#038;ssl=1\" alt=\"\" class=\"wp-image-2511\" srcset=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-29.png?resize=1024%2C393&amp;ssl=1 1024w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-29.png?resize=300%2C115&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-29.png?resize=768%2C295&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-29.png?w=1144&amp;ssl=1 1144w\" sizes=\"auto, (max-width: 678px) 100vw, 678px\" \/><\/a><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>ANALYZE (VERBOSE) ONLY vendas_part;   -- s\u00f3 as estat\u00edsticas globais da m\u00e3e<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-30.png?ssl=1\" rel=\"lightbox[2495]\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"678\" height=\"131\" src=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-30.png?resize=678%2C131&#038;ssl=1\" alt=\"\" class=\"wp-image-2512\" srcset=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-30.png?resize=1024%2C198&amp;ssl=1 1024w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-30.png?resize=300%2C58&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-30.png?resize=768%2C148&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-30.png?w=1151&amp;ssl=1 1151w\" sizes=\"auto, (max-width: 678px) 100vw, 678px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">O recado pr\u00e1tico se seus scripts de manuten\u00e7\u00e3o foram escritos para vers\u00f5es antigas assumindo &#8220;ANALYZE na m\u00e3e \u00e9 barato, s\u00f3 atualiza o global&#8221;, <strong>revise antes de migrar para o 18<\/strong> o mesmo comando agora percorre todas as parti\u00e7\u00f5es, e numa tabela com centenas de filhas a diferen\u00e7a de dura\u00e7\u00e3o \u00e9 brutal.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">Traduzindo para SQL Server\u00eas:<\/mark><\/strong> \u00e9 como se o <code>UPDATE STATISTICS<\/code> na tabela particionada passasse, de uma vers\u00e3o para outra, a reconstruir tamb\u00e9m todas as estat\u00edsticas incrementais de cada parti\u00e7\u00e3o. \u00d3timo para consist\u00eancia, perigoso para a janela de manuten\u00e7\u00e3o de quem n\u00e3o leu o release notes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Terceiro: o ponto cego que CONTINUA no PG 18<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Uma coisa o PostgreSQL 18 <strong>n\u00e3o<\/strong> mudou, e \u00e9 a pegadinha mais importante desta se\u00e7\u00e3o:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>O autovacuum n\u00e3o roda auto-analyze na estrutura-m\u00e3e. Nunca.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As filhas, sim cada uma \u00e9 uma tabela comum, tem seu contador de modifica\u00e7\u00f5es e entra na matem\u00e1tica do t\u00f3pico 6 normalmente. Mas a m\u00e3e n\u00e3o recebe INSERT\/UPDATE\/DELETE diretamente (os dados vivem nas filhas), ent\u00e3o o contador dela <strong>nunca estoura o limite<\/strong> e as estat\u00edsticas globais do conjunto v\u00e3o apodrecendo em sil\u00eancio, mesmo com o autovacuum funcionando perfeitamente.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>SELECT relname, last_autoanalyze<br>FROM pg_stat_user_tables<br>WHERE relname LIKE 'vendas_part%';<\/code><\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-31.png?ssl=1\" rel=\"lightbox[2495]\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"363\" height=\"131\" src=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-31.png?resize=363%2C131&#038;ssl=1\" alt=\"\" class=\"wp-image-2513\" srcset=\"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-31.png?w=363&amp;ssl=1 363w, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-31.png?resize=300%2C108&amp;ssl=1 300w\" sizes=\"auto, (max-width: 363px) 100vw, 363px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Consequ\u00eancia direta em ambiente particionado, <strong><code>ANALYZE<\/code> agendado na m\u00e3e n\u00e3o \u00e9 otimiza\u00e7\u00e3o, \u00e9 obriga\u00e7\u00e3o<\/strong> via cron, pg_cron ou a ferramenta de agendamento da casa. Sem isso, as consultas que cruzam parti\u00e7\u00f5es planejam com estat\u00edsticas do dia da carga inicial.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">Traduzindo para SQL Server\u00eas:<\/mark><\/strong> lembra do problema do Post 01, em que o threshold de auto update olhava a tabela inteira e uma parti\u00e7\u00e3o nova nunca &#8220;pesava&#8221; o suficiente para disparar a atualiza\u00e7\u00e3o? Aqui \u00e9 o primo dele, ainda mais radical n\u00e3o \u00e9 que o gatilho da m\u00e3e demora \u00e9 que ele <strong>n\u00e3o existe<\/strong>. A solu\u00e7\u00e3o \u00e9 a mesma que aplicamos no mundo manuten\u00e7\u00e3o de estat\u00edsticas agendada e consciente da estrutura de parti\u00e7\u00f5es.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Quarto: estat\u00edsticas agora sobrevivem ao upgrade<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A segunda novidade do PG18 resolve um rito de passagem que todo DBA PostgreSQL conhecia e que surpreende quem vem do SQL Server.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Como era (at\u00e9 o PG 17):<\/strong> o <code>pg_upgrade<\/code> a ferramenta de upgrade de vers\u00e3o maior migrava os dados, mas <strong>jogava fora todas as estat\u00edsticas do planner<\/strong>. O cluster novo subia funcionando, por\u00e9m &#8220;cego&#8221;: todo plano era chute at\u00e9 voc\u00ea rodar um ANALYZE geral na base inteira. Existia at\u00e9 um ritual p\u00f3s-upgrade (<code>vacuumdb --analyze-in-stages<\/code>) que gerava estat\u00edsticas grosseiras primeiro e refinava depois, s\u00f3 para a base n\u00e3o agonizar nas primeiras horas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Como ficou (PG 18):<\/strong> as estat\u00edsticas s\u00e3o <strong>preservadas durante o upgrade<\/strong>. O cluster novo executa enxergando o que o antigo enxergava, e a janela de degrada\u00e7\u00e3o p\u00f3s-upgrade praticamente desaparece.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\"><strong>Traduzindo para SQL Server\u00eas:<\/strong> <\/mark>voc\u00ea provavelmente nunca pensou nisso, porque no SQL Server as estat\u00edsticas sempre sobreviveram a upgrade, restore e attach elas moram dentro do banco de dados. No PostgreSQL elas moram em cat\u00e1logos do cluster que o pg_upgrade historicamente n\u00e3o levava junto. O PG18 corrige essa surpresa desagrad\u00e1vel e tira um item inteiro do runbook de migra\u00e7\u00e3o.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-cyan-blue-color\">Resumo em uma tela<\/mark><\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Pergunta<\/th><th>Resposta no PostgreSQL<\/th><\/tr><\/thead><tbody><tr><td>Quem coleta?<\/td><td><code>ANALYZE<\/code> (manual) ou autovacuum (auto-analyze)<\/td><\/tr><tr><td>Quanto l\u00ea?<\/td><td>300 \u00d7 statistics_target linhas (padr\u00e3o: 30.000)<\/td><\/tr><tr><td>Onde guarda?<\/td><td>Cat\u00e1logo <code>pg_statistic<\/code> \u2192 view <code>pg_stats<\/code><\/td><\/tr><tr><td>O que guarda?<\/td><td>MCV + freqs, histogram_bounds, n_distinct, correlation<\/td><\/tr><tr><td>Quando atualiza sozinho?<\/td><td>modifica\u00e7\u00f5es &gt; 50 + 10% da tabela<\/td><\/tr><tr><td>Ponto cego cl\u00e1ssico?<\/td><td>Estrutura-m\u00e3e de particionada (sem auto-analyze)<\/td><\/tr><tr><td>Bot\u00e3o de precis\u00e3o?<\/td><td><code>ALTER TABLE ... SET STATISTICS n<\/code> (por coluna)<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">No Post 03, coloco os dois lado a lado: histograma de 200 steps vs MCV+buckets, thresholds din\u00e2micos vs scale_factor, e o que cada engine podia aprender com a outra.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Reproduza em casa<\/h3>\n\n\n\n<h2 class=\"wp-block-heading\"><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-cyan-blue-color\">Fontes:<\/mark><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>PostgreSQL 18 Docs \u2014 <a href=\"https:\/\/www.postgresql.org\/docs\/18\/sql-analyze.html\" target=\"_blank\" rel=\"noopener\">ANALYZE<\/a> (statistics_target, amostragem, MCV e histograma)<\/li>\n\n\n\n<li>PostgreSQL 18 Docs \u2014 <a href=\"https:\/\/www.postgresql.org\/docs\/18\/planner-stats.html\" target=\"_blank\" rel=\"noopener\">Cap. 14.2: Statistics Used by the Planner<\/a><\/li>\n\n\n\n<li>PostgreSQL 18 Docs \u2014 <a href=\"https:\/\/www.postgresql.org\/docs\/18\/row-estimation-examples.html\" target=\"_blank\" rel=\"noopener\">Cap. 14.3: Row Estimation Examples<\/a> (como MCV\/histograma viram estimativa)<\/li>\n\n\n\n<li>PostgreSQL 18 Docs \u2014 <a href=\"https:\/\/www.postgresql.org\/docs\/18\/view-pg-stats.html\" target=\"_blank\" rel=\"noopener\">pg_stats view<\/a><\/li>\n\n\n\n<li>PostgreSQL 18 Docs \u2014 <a href=\"https:\/\/www.postgresql.org\/docs\/18\/routine-vacuuming.html#AUTOVACUUM\" target=\"_blank\" rel=\"noopener\">Routine Vacuuming: The Autovacuum Daemon<\/a> (f\u00f3rmula do auto-analyze)<\/li>\n\n\n\n<li><a href=\"https:\/\/www.postgresql.org\/docs\/release\/18.0\/\" target=\"_blank\" rel=\"noopener\">PostgreSQL 18 Release Notes<\/a> e <a href=\"https:\/\/www.postgresql.org\/about\/news\/postgresql-18-released-3142\/\" target=\"_blank\" rel=\"noopener\">an\u00fancio oficial<\/a> (ANALYZE recursivo em particionadas; estat\u00edsticas preservadas no pg_upgrade)<\/li>\n\n\n\n<li>C\u00f3digo-fonte \u2014 <a href=\"https:\/\/github.com\/postgres\/postgres\/blob\/master\/src\/backend\/commands\/analyze.c\" target=\"_blank\" rel=\"noopener\"><code>src\/backend\/commands\/analyze.c<\/code><\/a> (origem do fator 300, com refer\u00eancia ao paper de 1998)<\/li>\n\n\n\n<li>Microsoft Learn \u2014 <a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/relational-databases\/statistics\/statistics\" target=\"_blank\" rel=\"noopener\">Statistics (SQL Server)<\/a> (thresholds de auto update, para as compara\u00e7\u00f5es)<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Como o PostgreSQL enxerga seus dados? Destrincho o ANALYZE e sua amostra de 30 mil linhas, as quatro pe\u00e7as do pg_stats (MCVs, histograma, n_distinct, correlation), a matem\u00e1tica do autovacuum e as novidades do PostgreSQL 18 \u2014 tudo validado em ambiente real e traduzido para o SQL Server\u00eas.<\/p>\n","protected":false},"author":1,"featured_media":2514,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"rop_custom_images_group":[],"rop_custom_messages_group":[],"rop_publish_now":"initial","rop_publish_now_accounts":{"twitter_91251433_91251433":""},"rop_publish_now_history":[],"rop_publish_now_status":"pending","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"Como o PostgreSQL enxerga seus dados? Destrincho o ANALYZE e sua amostra de 30 mil linhas, as quatro pe\u00e7as do pg_stats (MCVs, histograma, n_distinct, correlation), a matem\u00e1tica do autovacuum e as novidades do PostgreSQL 18 tudo validado em ambiente real e traduzido para o SQL Server\u00eas.","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false,"_wpscppro_dont_share_socialmedia":false,"_wpscppro_custom_social_share_image":0,"_facebook_share_type":"default","_twitter_share_type":"default","_linkedin_share_type":"default","_pinterest_share_type":"default","_linkedin_share_type_page":"","_instagram_share_type":"default","_medium_share_type":"default","_threads_share_type":"default","_google_business_share_type":"default","_bluesky_share_type":"default","_selected_social_profile":[],"_wpsp_enable_custom_social_template":false,"_wpsp_social_scheduling":{"enabled":false,"datetime":null,"platforms":[],"status":"template_only","dateOption":"today","timeOption":"now","customDays":"","customHours":"","customDate":"","customTime":"","schedulingType":"absolute"},"_wpsp_active_default_template":true},"categories":[689,239],"tags":[707,711,703,54,675,715],"class_list":["post-2495","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-postgresql","category-tuning","tag-analyze-postgresql","tag-autovacuum-analyze","tag-estatisticas-postgresql","tag-performance","tag-postgresql","tag-postgresql-18"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/image-32.png?fit=1600%2C900&ssl=1","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p6eIyh-Ef","jetpack-related-posts":[{"id":2463,"url":"https:\/\/www.tiagoneves.net\/blog\/conversao-implicita-no-postgresql-por-que-o-comportamento-e-diferente-do-sql-server-e-onde-a-performance-realmente-morre\/","url_meta":{"origin":2495,"position":0},"title":"Convers\u00e3o impl\u00edcita no PostgreSQL: por que o comportamento \u00e9 diferente do SQL Server (e onde a performance realmente morre)","author":"tiagoneves","date":"14 de julho de 2026","format":false,"excerpt":"O resumo (excerpt) que sugeri no kit SEO: O blog agora tamb\u00e9m fala PostgreSQL! No primeiro post da s\u00e9rie SQL Server \u00d7 PostgreSQL, desmontamos um mito sobre casts, mostramos onde os \u00edndices morrem de verdade (com EXPLAIN ANALYZE em 100 mil linhas) e apresentamos a arma secreta do PG: o\u2026","rel":"","context":"Em &quot;PostgreSQL&quot;","block_context":{"text":"PostgreSQL","link":"https:\/\/www.tiagoneves.net\/blog\/category\/postgresql\/"},"img":{"alt_text":"Convers\u00e3o impl\u00edcita: duas filosofias, dois caminhos \u2014 SQL Server \u00d7 PostgreSQL","src":"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/og-conversao-implicita-postgresql-1200x630-1.png?fit=1200%2C630&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/og-conversao-implicita-postgresql-1200x630-1.png?fit=1200%2C630&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/og-conversao-implicita-postgresql-1200x630-1.png?fit=1200%2C630&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/og-conversao-implicita-postgresql-1200x630-1.png?fit=1200%2C630&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/og-conversao-implicita-postgresql-1200x630-1.png?fit=1200%2C630&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":2487,"url":"https:\/\/www.tiagoneves.net\/blog\/estatisticas-no-sql-server-por-que-seus-planos-de-execucao-comecam-aqui\/","url_meta":{"origin":2495,"position":1},"title":"Estat\u00edsticas no SQL Server: por que seus planos de execu\u00e7\u00e3o come\u00e7am aqui","author":"tiagoneves","date":"22 de julho de 2026","format":false,"excerpt":"Post 01 de 03 da s\u00e9rie Estat\u00edsticas: SQL Server \u00d7 PostgreSQL O sintoma que ningu\u00e9m questiona Toda madrugada, \u00e0s 02h00, uma rotina de UPDATE STATISTICS entra em execu\u00e7\u00e3o em um ambiente que administro. A tabela principal tem cerca de 4 bilh\u00f5es de linhas e 2 TB, particionada por per\u00edodo. A\u2026","rel":"","context":"Em &quot;SQLServer Geral&quot;","block_context":{"text":"SQLServer Geral","link":"https:\/\/www.tiagoneves.net\/blog\/category\/sqlserver-geral\/"},"img":{"alt_text":"Toda madrugada, uma rotina varria 2,8 bilh\u00f5es de linhas para atualizar estat\u00edsticas de dados que n\u00e3o mudaram. Neste primeiro post da s\u00e9rie SQL Server \u00d7 PostgreSQL, mergulho no objeto que decide todos os seus planos de execu\u00e7\u00e3o: histograma, density vector, thresholds de auto update e o que o INCREMENTAL = ON realmente faz (e o que n\u00e3o faz) em tabelas particionadas.","src":"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/featured-post1-estatisticas-sqlserver.png?fit=1200%2C630&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/featured-post1-estatisticas-sqlserver.png?fit=1200%2C630&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/featured-post1-estatisticas-sqlserver.png?fit=1200%2C630&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/featured-post1-estatisticas-sqlserver.png?fit=1200%2C630&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/07\/featured-post1-estatisticas-sqlserver.png?fit=1200%2C630&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":2441,"url":"https:\/\/www.tiagoneves.net\/blog\/conversao-implicita-no-sql-server-o-vilao-invisivel-da-performance\/","url_meta":{"origin":2495,"position":2},"title":"Convers\u00e3o Impl\u00edcita no SQL Server: O Vil\u00e3o Invis\u00edvel da Performance","author":"tiagoneves","date":"2 de julho de 2026","format":false,"excerpt":"Quando falamos em performance no SQL Server, normalmente pensamos em \u00edndices ausentes, estat\u00edsticas desatualizadas ou consultas mal escritas. Mas existe um problema extremamente comum e muitas vezes ignorado capaz de transformar uma consulta r\u00e1pida em um gargalo: as convers\u00f5es impl\u00edcitas (implicit conversions). Uma simples diferen\u00e7a entre os tipos de dados\u2026","rel":"","context":"Em &quot;SQLServer Geral&quot;","block_context":{"text":"SQLServer Geral","link":"https:\/\/www.tiagoneves.net\/blog\/category\/sqlserver-geral\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/06\/image.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/06\/image.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/06\/image.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/06\/image.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/06\/image.png?resize=1050%2C600&ssl=1 3x"},"classes":[]},{"id":2431,"url":"https:\/\/www.tiagoneves.net\/blog\/atualizacao-estatisticas-sql-server-vldb\/","url_meta":{"origin":2495,"position":3},"title":"Estat\u00edsticas SQL Server em VLDB: Por Que Atualizar Pode Ser Melhor Que Rebuild de \u00cdndices","author":"tiagoneves","date":"1 de junho de 2026","format":false,"excerpt":"Descubra por que a atualiza\u00e7\u00e3o de estat\u00edsticas SQL Server pode gerar mais performance que rebuild de \u00edndices em ambientes VLDB e janelas curtas de manuten\u00e7\u00e3o.","rel":"","context":"Em &quot;Tuning&quot;","block_context":{"text":"Tuning","link":"https:\/\/www.tiagoneves.net\/blog\/category\/tuning\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/06\/StatisticsXRebuild.png?resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/06\/StatisticsXRebuild.png?resize=350%2C200 1x, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/06\/StatisticsXRebuild.png?resize=525%2C300 1.5x, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2026\/06\/StatisticsXRebuild.png?resize=700%2C400 2x"},"classes":[]},{"id":2352,"url":"https:\/\/www.tiagoneves.net\/blog\/alwayson-boas-praticas-para-instalar-o-cumulative-update\/","url_meta":{"origin":2495,"position":4},"title":"AlwaysOn &#8211; Boas Pr\u00e1ticas para instalar o Cumulative Update","author":"tiagoneves","date":"2 de julho de 2020","format":false,"excerpt":"Ol\u00e1 pessoal tudo certo? No post de hoje vou falar um pouco mais sobre o SQL Server AlwaysOn, onde vi em alguns f\u00f3runs uma d\u00favida bem comum: \u201cQual \u00e9 a melhor forma de atualizar o SQL Server em um ambiente com o AlwaysOn habilitado?\u201d Mas antes, se voc\u00ea quiser ver\u2026","rel":"","context":"Em &quot;High Availability&quot;","block_context":{"text":"High Availability","link":"https:\/\/www.tiagoneves.net\/blog\/category\/high-availability\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2020\/06\/image-12.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2020\/06\/image-12.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2020\/06\/image-12.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2020\/06\/image-12.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":1495,"url":"https:\/\/www.tiagoneves.net\/blog\/dicas-de-como-realizar-um-tuning-no-sql-server\/","url_meta":{"origin":2495,"position":5},"title":"Dicas de como realizar um tuning no SQL Server","author":"tiagoneves","date":"9 de maio de 2019","format":false,"excerpt":"Ol\u00e1 pessoal tudo certo? No post de hoje, eu quero compartilhar com voc\u00eas algumas dicas de como iniciar um tuning em alguma rotina, seja stored procedure, function ou query adhoc. Quando vamos iniciar um trabalho de tuning, uma das primeiras informa\u00e7\u00f5es que precisamos \u00e9 visualizar o plano de execu\u00e7\u00e3o da\u2026","rel":"","context":"Em &quot;Casos do dia-a-dia&quot;","block_context":{"text":"Casos do dia-a-dia","link":"https:\/\/www.tiagoneves.net\/blog\/category\/casos-do-dia-a-dia\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.tiagoneves.net\/blog\/wp-content\/uploads\/2019\/05\/word-image-3-1.png?fit=352%2C278&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]}],"jetpack_likes_enabled":true,"_links":{"self":[{"href":"https:\/\/www.tiagoneves.net\/blog\/wp-json\/wp\/v2\/posts\/2495","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tiagoneves.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tiagoneves.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tiagoneves.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tiagoneves.net\/blog\/wp-json\/wp\/v2\/comments?post=2495"}],"version-history":[{"count":2,"href":"https:\/\/www.tiagoneves.net\/blog\/wp-json\/wp\/v2\/posts\/2495\/revisions"}],"predecessor-version":[{"id":2516,"href":"https:\/\/www.tiagoneves.net\/blog\/wp-json\/wp\/v2\/posts\/2495\/revisions\/2516"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.tiagoneves.net\/blog\/wp-json\/wp\/v2\/media\/2514"}],"wp:attachment":[{"href":"https:\/\/www.tiagoneves.net\/blog\/wp-json\/wp\/v2\/media?parent=2495"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tiagoneves.net\/blog\/wp-json\/wp\/v2\/categories?post=2495"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tiagoneves.net\/blog\/wp-json\/wp\/v2\/tags?post=2495"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}