tableStats

Return the statistics PostgreSQL keeps for one table: estimated row count, seq_scan and idx_scan counts, live and dead tuples, rows modified since the last analyze, rows inserted since the last vacuum, the manual and automatic vacuum and analyze times as four separate fields…

read-onlyany connectionsweeps instancesweeps replication groupsweeps groupcan return data values

Synopsis

tableStats(schema, table)

Description

Return the statistics PostgreSQL keeps for one table: estimated row count, seq_scan and idx_scan counts, live and dead tuples, rows modified since the last analyze, rows inserted since the last vacuum, the manual and automatic vacuum and analyze times as four separate fields (last_vacuum and last_analyze are the manual ones, exactly as in pg_stat_user_tables -- a recent last_vacuum beside a null last_autovacuum means the table is being kept alive by hand and autovacuum is not reaching it), per-index scan counts, and the per-column pg_stats histograms (null_frac, avg_width, n_distinct, physical order correlation, most_common_vals and their frequencies, and three points off the histogram -- histogram_bounds gives low, mid and high, the observed extremes and median of the distribution, which are real values that occur in the column and so are usable directly as parameters to re-plan a statement with.

It is three points rather than the whole array because the array is statistics_target+1 entries wide, 101 by default; for the whole distribution of one column call columnHistogram. Null when the column has no histogram, meaning every value is in the MCV list or the column was never analyzed). IMPORTANT: pg_stats returns no row at all for a table whose row-level security is active for the connecting role, so on such a table every per-column statistic here is null and looks exactly like a table nobody has analyzed -- stats_hidden_by_rls says which it is, and the analyze timestamps beside it prove the statistics exist.

Reads the catalog and the statistics collector only -- no relation is opened and no file is measured. counters_since is when this database's statistics were last reset; seq_scan, idx_scan and the tuple counters cover only the period since, so a zero means 'not since then' rather than 'never'. size_estimate is relpages*block_size (the server's BLCKSZ, 8192 unless it was built otherwise) and is only as fresh as estimated_from says: for a measured size call tableSize.

Note that most_common_vals and histogram_bounds both contain literal values sampled from the column. Not to be confused with tableIOStats, which reports pg_statio_all_tables -- whether reads came from the buffer cache or the disk.

This operation can return values that came from your data. It never changes anything and never returns rows, but read what reaches the caller before connecting it to a database whose contents are sensitive.

Parameters

schema requiredstring
table requiredstring

Also accepts connection, instance, replication_group, group, role, described once under arguments every tool takes.

Output

One table's statistics. An absent extension or a missing grant is reported as {error, hint} instead.

FieldType
columnsobject | null
idx_scaninteger | null
indexesobject | null
n_dead_tupinteger | null
n_ins_since_vacuuminteger | null
n_live_tupinteger | null
n_mod_since_analyzeinteger | null
rowsnumber | null
seq_scaninteger | null
size_estimateinteger | null
tablestring | null

Scope

The counters here are each server's own, so members of a replication group legitimately disagree and the answer is their sum, not the primary's copy. Dead tuples and the last vacuum and analyze times describe work that only happens on a primary; on a replica they are noise, not a second opinion.

Example mocked data

Request

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "tableStats",
    "arguments": {
      "schema": "shop",
      "table": "orders"
    }
  }
}

Result

{
  "table": "shop.orders",
  "rows": 1204388,
  "size_estimate": 187351040,
  "seq_scan": 41,
  "idx_scan": 9832117,
  "n_live_tup": 1204102,
  "n_dead_tup": 18344,
  "n_mod_since_analyze": 22410,
  "n_ins_since_vacuum": 9120,
  "columns": {
    "status": {
      "null_frac": 0.0,
      "n_distinct": 3,
      "most_common_vals": [
        "shipped",
        "paid",
        "pending"
      ],
      "most_common_freqs": [
        0.71,
        0.24,
        0.05
      ]
    }
  },
  "indexes": {
    "orders_pkey": {
      "idx_scan": 9120004
    },
    "orders_customer_id_idx": {
      "idx_scan": 712113
    }
  }
}

Invented values on a fictional shop database, shaped by and checked against this tool's output schema. Real output is returned as structuredContent to clients that negotiate MCP 2025-06-18 or later.

See also

listSchemas, listTables, tableDetails, largeObjects, listPartitions, partitionDetails, listTableStats, tableSize, listTableSizes, listFunctions, functionDetails, listEnums, enumDetails, listTypes, typeDetails, columnHistogram, listExtendedStatistics, listSequences