Skip to content

Commit e120c9a

Browse files
committed
rework b2
1 parent 126e86b commit e120c9a

File tree

1 file changed

+106
-83
lines changed

1 file changed

+106
-83
lines changed

sql/b2_btree_estimation.sql

Lines changed: 106 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -4,95 +4,118 @@
44

55
-- WARNING: executed with a non-superuser role, the query inspect only index on tables you are granted to read.
66
-- WARNING: rows with is_na = 't' are known to have bad statistics ("name" type is not supported).
7-
-- This query is compatible with PostgreSQL 8.2 and after
7+
-- This query is compatible with PostgreSQL 8.2+
88

9-
with data as (
10-
SELECT current_database(), nspname AS schemaname, tblname, idxname, bs*(relpages)::bigint AS real_size,
9+
with step1 as (
10+
select
11+
i.nspname as schema_name,
12+
i.tblname as table_name,
13+
i.idxname as index_name,
14+
i.reltuples,
15+
i.relpages,
16+
i.relam,
17+
a.attrelid AS table_oid,
18+
current_setting('block_size')::numeric AS bs,
19+
fillfactor,
20+
-- MAXALIGN: 4 on 32bits, 8 on 64bits (and mingw32 ?)
21+
case when version() ~ 'mingw32|64-bit|x86_64|ppc64|ia64|amd64' then 8 else 4 end as maxalign,
22+
/* per page header, fixed size: 20 for 7.X, 24 for others */
23+
24 AS pagehdr,
24+
/* per page btree opaque data */
25+
16 AS pageopqdata,
26+
/* per tuple header: add IndexAttributeBitMapData if some cols are null-able */
27+
case
28+
when max(coalesce(s.null_frac,0)) = 0 then 2 -- IndexTupleData size
29+
else 2 + (( 32 + 8 - 1 ) / 8) -- IndexTupleData size + IndexAttributeBitMapData size ( max num filed per index + 8 - 1 /8)
30+
end as index_tuple_hdr_bm,
31+
/* data len: we remove null values save space using it fractionnal part from stats */
32+
sum((1 - coalesce(s.null_frac, 0)) * coalesce(s.avg_width, 1024)) as nulldatawidth,
33+
max(case when a.atttypid = 'pg_catalog.name'::regtype then 1 else 0 end) > 0 as is_na
34+
from pg_attribute as a
35+
join (
36+
select
37+
nspname, tbl.relname AS tblname, idx.relname AS idxname, idx.reltuples, idx.relpages, idx.relam,
38+
indrelid, indexrelid, indkey::smallint[] AS attnum,
39+
coalesce(substring(array_to_string(idx.reloptions, ' ') from 'fillfactor=([0-9]+)')::smallint, 90) as fillfactor
40+
from pg_index
41+
join pg_class idx on idx.oid = pg_index.indexrelid
42+
join pg_class tbl on tbl.oid = pg_index.indrelid
43+
join pg_namespace on pg_namespace.oid = idx.relnamespace
44+
where pg_index.indisvalid AND tbl.relkind = 'r' AND idx.relpages > 0
45+
) as i on a.attrelid = i.indexrelid
46+
join pg_stats as s on
47+
s.schemaname = i.nspname
48+
and (
49+
(s.tablename = i.tblname and s.attname = pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, true)) -- stats from tbl
50+
OR (s.tablename = i.idxname AND s.attname = a.attname) -- stats from functionnal cols
51+
)
52+
join pg_type as t on a.atttypid = t.oid
53+
where a.attnum > 0
54+
group by 1, 2, 3, 4, 5, 6, 7, 8, 9
55+
), step2 as (
56+
select
57+
*,
58+
(
59+
index_tuple_hdr_bm + maxalign
60+
-- Add padding to the index tuple header to align on MAXALIGN
61+
- case when index_tuple_hdr_bm % maxalign = 0 THEN maxalign else index_tuple_hdr_bm % maxalign end
62+
+ nulldatawidth + maxalign
63+
-- Add padding to the data to align on MAXALIGN
64+
- case
65+
when nulldatawidth = 0 then 0
66+
when nulldatawidth::integer % maxalign = 0 then maxalign
67+
else nulldatawidth::integer % maxalign
68+
end
69+
)::numeric as nulldatahdrwidth
70+
-- , index_tuple_hdr_bm, nulldatawidth -- (DEBUG INFO)
71+
from step1
72+
), step3 as (
73+
select
74+
*,
75+
-- ItemIdData size + computed avg size of a tuple (nulldatahdrwidth)
76+
coalesce(1 + ceil(reltuples / floor((bs - pageopqdata - pagehdr) / (4 + nulldatahdrwidth)::float)), 0) as est_pages,
77+
coalesce(1 + ceil(reltuples / floor((bs - pageopqdata - pagehdr) * fillfactor / (100 * (4 + nulldatahdrwidth)::float))), 0) as est_pages_ff
78+
-- , stattuple.pgstatindex(quote_ident(nspname)||'.'||quote_ident(idxname)) AS pst, index_tuple_hdr_bm, maxalign, pagehdr, nulldatawidth, nulldatahdrwidth, reltuples -- (DEBUG INFO)
79+
from step2
80+
join pg_am am on step2.relam = am.oid
81+
where am.amname = 'btree'
82+
), step4 as (
83+
SELECT
84+
*,
85+
bs*(relpages)::bigint AS real_size,
86+
-------current_database(), nspname AS schemaname, tblname, idxname, bs*(relpages)::bigint AS real_size,
1187
bs*(relpages-est_pages)::bigint AS extra_size,
1288
100 * (relpages-est_pages)::float / relpages AS extra_ratio,
13-
fillfactor, bs*(relpages-est_pages_ff) AS bloat_size,
14-
100 * (relpages-est_pages_ff)::float / relpages AS bloat_ratio,
15-
is_na
89+
bs*(relpages-est_pages_ff) AS bloat_size,
90+
100 * (relpages-est_pages_ff)::float / relpages AS bloat_ratio
1691
-- , 100-(sub.pst).avg_leaf_density, est_pages, index_tuple_hdr_bm, maxalign, pagehdr, nulldatawidth, nulldatahdrwidth, sub.reltuples, sub.relpages -- (DEBUG INFO)
17-
FROM (
18-
SELECT coalesce(1 +
19-
ceil(reltuples/floor((bs-pageopqdata-pagehdr)/(4+nulldatahdrwidth)::float)), 0 -- ItemIdData size + computed avg size of a tuple (nulldatahdrwidth)
20-
) AS est_pages,
21-
coalesce(1 +
22-
ceil(reltuples/floor((bs-pageopqdata-pagehdr)*fillfactor/(100*(4+nulldatahdrwidth)::float))), 0
23-
) AS est_pages_ff,
24-
bs, nspname, table_oid, tblname, idxname, relpages, fillfactor, is_na
25-
-- , stattuple.pgstatindex(quote_ident(nspname)||'.'||quote_ident(idxname)) AS pst, index_tuple_hdr_bm, maxalign, pagehdr, nulldatawidth, nulldatahdrwidth, reltuples -- (DEBUG INFO)
26-
FROM (
27-
SELECT maxalign, bs, nspname, tblname, idxname, reltuples, relpages, relam, table_oid, fillfactor,
28-
( index_tuple_hdr_bm +
29-
maxalign - CASE -- Add padding to the index tuple header to align on MAXALIGN
30-
WHEN index_tuple_hdr_bm%maxalign = 0 THEN maxalign
31-
ELSE index_tuple_hdr_bm%maxalign
32-
END
33-
+ nulldatawidth + maxalign - CASE -- Add padding to the data to align on MAXALIGN
34-
WHEN nulldatawidth = 0 THEN 0
35-
WHEN nulldatawidth::integer%maxalign = 0 THEN maxalign
36-
ELSE nulldatawidth::integer%maxalign
37-
END
38-
)::numeric AS nulldatahdrwidth, pagehdr, pageopqdata, is_na
39-
-- , index_tuple_hdr_bm, nulldatawidth -- (DEBUG INFO)
40-
FROM (
41-
SELECT
42-
i.nspname, i.tblname, i.idxname, i.reltuples, i.relpages, i.relam, a.attrelid AS table_oid,
43-
current_setting('block_size')::numeric AS bs, fillfactor,
44-
CASE -- MAXALIGN: 4 on 32bits, 8 on 64bits (and mingw32 ?)
45-
WHEN version() ~ 'mingw32' OR version() ~ '64-bit|x86_64|ppc64|ia64|amd64' THEN 8
46-
ELSE 4
47-
END AS maxalign,
48-
/* per page header, fixed size: 20 for 7.X, 24 for others */
49-
24 AS pagehdr,
50-
/* per page btree opaque data */
51-
16 AS pageopqdata,
52-
/* per tuple header: add IndexAttributeBitMapData if some cols are null-able */
53-
CASE WHEN max(coalesce(s.null_frac,0)) = 0
54-
THEN 2 -- IndexTupleData size
55-
ELSE 2 + (( 32 + 8 - 1 ) / 8) -- IndexTupleData size + IndexAttributeBitMapData size ( max num filed per index + 8 - 1 /8)
56-
END AS index_tuple_hdr_bm,
57-
/* data len: we remove null values save space using it fractionnal part from stats */
58-
sum( (1-coalesce(s.null_frac, 0)) * coalesce(s.avg_width, 1024)) AS nulldatawidth,
59-
max( CASE WHEN a.atttypid = 'pg_catalog.name'::regtype THEN 1 ELSE 0 END ) > 0 AS is_na
60-
FROM pg_attribute AS a
61-
JOIN (
62-
SELECT nspname, tbl.relname AS tblname, idx.relname AS idxname, idx.reltuples, idx.relpages, idx.relam,
63-
indrelid, indexrelid, indkey::smallint[] AS attnum,
64-
coalesce(substring(
65-
array_to_string(idx.reloptions, ' ')
66-
from 'fillfactor=([0-9]+)')::smallint, 90) AS fillfactor
67-
FROM pg_index
68-
JOIN pg_class idx ON idx.oid=pg_index.indexrelid
69-
JOIN pg_class tbl ON tbl.oid=pg_index.indrelid
70-
JOIN pg_namespace ON pg_namespace.oid = idx.relnamespace
71-
WHERE pg_index.indisvalid AND tbl.relkind = 'r' AND idx.relpages > 0
72-
) AS i ON a.attrelid = i.indexrelid
73-
JOIN pg_stats AS s ON s.schemaname = i.nspname
74-
AND ((s.tablename = i.tblname AND s.attname = pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, TRUE)) -- stats from tbl
75-
OR (s.tablename = i.idxname AND s.attname = a.attname))-- stats from functionnal cols
76-
JOIN pg_type AS t ON a.atttypid = t.oid
77-
WHERE a.attnum > 0
78-
GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9
79-
) AS s1
80-
) AS s2
81-
JOIN pg_am am ON s2.relam = am.oid WHERE am.amname = 'btree'
82-
) AS sub
92+
from step3
8393
-- WHERE NOT is_na
8494
)
8595
select
86-
current_database, schemaname, tblname, idxname,
87-
real_size, pg_size_pretty(real_size::numeric) as real_size_pretty,
88-
extra_size, pg_size_pretty(extra_size::numeric) as extra_size_pretty,
89-
extra_ratio as "extra_ratio, %",
90-
bloat_size, pg_size_pretty(bloat_size::numeric) as bloat_size_pretty,
91-
bloat_ratio as "bloat_ratio, %",
92-
fillfactor,
93-
is_na,
94-
real_size - bloat_size as live_data_size
95-
from data
96-
order by bloat_size desc
96+
case is_na when true then 'TRUE' else '' end as "Is N/A",
97+
format(
98+
$out$%s
99+
(%s)$out$,
100+
left(index_name, 50) || case when length(index_name) > 50 then '' else '' end,
101+
coalesce(nullif(schema_name, 'public') || '.', '') || table_name
102+
) as "Index (Table)",
103+
pg_size_pretty(real_size::numeric) as "Size",
104+
'~' || pg_size_pretty(extra_size::numeric)::text || ' (' || round(extra_ratio::numeric, 2)::text || '%)' as "Extra",
105+
'~' || pg_size_pretty(bloat_size::numeric)::text || ' (' || round(bloat_ratio::numeric, 2)::text || '%)' as "Bloat",
106+
'~' || pg_size_pretty((real_size - bloat_size)::numeric) as "Live",
107+
fillfactor
108+
\if :postgresdba_extended
109+
,
110+
schema_name,
111+
table_name,
112+
index_name,
113+
real_size as real_size_raw,
114+
extra_size as extra_size_raw,
115+
bloat_size as bloat_size_raw,
116+
real_size - bloat_size as live_data_size_raw
117+
\endif
118+
from step4
119+
order by real_size desc nulls last
97120
;
98121

0 commit comments

Comments
 (0)