|
1 | 1 | --Tables Bloat, rough estimation |
2 | 2 |
|
3 | | ---enhanced version of https://github.com/ioguix/pgsql-bloat-estimation/blob/master/table/table_bloat.sql |
| 3 | +--This SQL is derived from https://github.com/ioguix/pgsql-bloat-estimation/blob/master/table/table_bloat.sql |
4 | 4 |
|
5 | | -/* WARNING: executed with a non-superuser role, the query inspect only tables you are granted to read. |
| 5 | +/* |
| 6 | +* WARNING: executed with a non-superuser role, the query inspect only tables you are granted to read. |
6 | 7 | * This query is compatible with PostgreSQL 9.0 and more |
7 | 8 | */ |
8 | 9 |
|
9 | | -with data as ( |
10 | | - SELECT current_database(), schemaname, tblname, bs*tblpages AS real_size, |
11 | | - (tblpages-est_tblpages)*bs AS extra_size, |
12 | | - CASE WHEN tblpages - est_tblpages > 0 |
13 | | - THEN 100 * (tblpages - est_tblpages)/tblpages::float |
14 | | - ELSE 0 |
15 | | - END AS extra_ratio, fillfactor, (tblpages-est_tblpages_ff)*bs AS bloat_size, |
16 | | - CASE WHEN tblpages - est_tblpages_ff > 0 |
17 | | - THEN 100 * (tblpages - est_tblpages_ff)/tblpages::float |
18 | | - ELSE 0 |
19 | | - END AS bloat_ratio, is_na |
20 | | - -- , (pst).free_percent + (pst).dead_tuple_percent AS real_frag |
21 | | - FROM ( |
22 | | - SELECT ceil( reltuples / ( (bs-page_hdr)/tpl_size ) ) + ceil( toasttuples / 4 ) AS est_tblpages, |
23 | | - ceil( reltuples / ( (bs-page_hdr)*fillfactor/(tpl_size*100) ) ) + ceil( toasttuples / 4 ) AS est_tblpages_ff, |
24 | | - tblpages, fillfactor, bs, tblid, schemaname, tblname, heappages, toastpages, is_na |
25 | | - -- , stattuple.pgstattuple(tblid) AS pst |
26 | | - FROM ( |
27 | | - SELECT |
28 | | - ( 4 + tpl_hdr_size + tpl_data_size + (2*ma) |
29 | | - - CASE WHEN tpl_hdr_size%ma = 0 THEN ma ELSE tpl_hdr_size%ma END |
30 | | - - CASE WHEN ceil(tpl_data_size)::int%ma = 0 THEN ma ELSE ceil(tpl_data_size)::int%ma END |
31 | | - ) AS tpl_size, bs - page_hdr AS size_per_block, (heappages + toastpages) AS tblpages, heappages, |
32 | | - toastpages, reltuples, toasttuples, bs, page_hdr, tblid, schemaname, tblname, fillfactor, is_na |
33 | | - FROM ( |
34 | | - SELECT |
35 | | - tbl.oid AS tblid, ns.nspname AS schemaname, tbl.relname AS tblname, tbl.reltuples, |
36 | | - tbl.relpages AS heappages, coalesce(toast.relpages, 0) AS toastpages, |
37 | | - coalesce(toast.reltuples, 0) AS toasttuples, |
38 | | - coalesce(substring( |
39 | | - array_to_string(tbl.reloptions, ' ') |
40 | | - FROM '%fillfactor=#"__#"%' FOR '#')::smallint, 100) AS fillfactor, |
41 | | - current_setting('block_size')::numeric AS bs, |
42 | | - CASE WHEN version()~'mingw32' OR version()~'64-bit|x86_64|ppc64|ia64|amd64' THEN 8 ELSE 4 END AS ma, |
43 | | - 24 AS page_hdr, |
44 | | - 23 + CASE WHEN MAX(coalesce(null_frac,0)) > 0 THEN ( 7 + count(*) ) / 8 ELSE 0::int END |
45 | | - + CASE WHEN tbl.relhasoids THEN 4 ELSE 0 END AS tpl_hdr_size, |
46 | | - sum( (1-coalesce(s.null_frac, 0)) * coalesce(s.avg_width, 1024) ) AS tpl_data_size, |
47 | | - bool_or(att.atttypid = 'pg_catalog.name'::regtype) AS is_na |
48 | | - FROM pg_attribute AS att |
49 | | - JOIN pg_class AS tbl ON att.attrelid = tbl.oid |
50 | | - JOIN pg_namespace AS ns ON ns.oid = tbl.relnamespace |
51 | | - JOIN pg_stats AS s ON s.schemaname=ns.nspname |
52 | | - AND s.tablename = tbl.relname AND s.inherited=false AND s.attname=att.attname |
53 | | - LEFT JOIN pg_class AS toast ON tbl.reltoastrelid = toast.oid |
54 | | - WHERE att.attnum > 0 AND NOT att.attisdropped |
55 | | - AND tbl.relkind = 'r' |
56 | | - GROUP BY 1,2,3,4,5,6,7,8,9,10, tbl.relhasoids |
57 | | - ORDER BY 2,3 |
58 | | - ) AS s |
59 | | - ) AS s2 |
60 | | - ) AS s3 |
| 10 | + |
| 11 | +with step1 as ( |
| 12 | + select |
| 13 | + tbl.oid tblid, |
| 14 | + ns.nspname as schema_name, |
| 15 | + tbl.relname as table_name, |
| 16 | + tbl.reltuples, |
| 17 | + tbl.relpages as heappages, |
| 18 | + coalesce(toast.relpages, 0) as toastpages, |
| 19 | + coalesce(toast.reltuples, 0) as toasttuples, |
| 20 | + coalesce(substring(array_to_string(tbl.reloptions, ' ') from '%fillfactor=#"__#"%' for '#')::int2, 100) as fillfactor, |
| 21 | + current_setting('block_size')::numeric as bs, |
| 22 | + case when version() ~ 'mingw32|64-bit|x86_64|ppc64|ia64|amd64' then 8 else 4 end as ma, -- NS: TODO: check it |
| 23 | + 24 as page_hdr, |
| 24 | + 23 + case when max(coalesce(null_frac, 0)) > 0 then (7 + count(*)) / 8 else 0::int end |
| 25 | + + case when tbl.relhasoids then 4 else 0 end as tpl_hdr_size, |
| 26 | + sum((1 - coalesce(s.null_frac, 0)) * coalesce(s.avg_width, 1024)) as tpl_data_size, |
| 27 | + bool_or(att.atttypid = 'pg_catalog.name'::regtype) or count(att.attname) <> count(s.attname) as is_na |
| 28 | + from pg_attribute as att |
| 29 | + join pg_class as tbl on att.attrelid = tbl.oid and tbl.relkind = 'r' |
| 30 | + join pg_namespace as ns on ns.oid = tbl.relnamespace |
| 31 | + join pg_stats as s on s.schemaname = ns.nspname and s.tablename = tbl.relname and not s.inherited and s.attname = att.attname |
| 32 | + left join pg_class as toast on tbl.reltoastrelid = toast.oid |
| 33 | + where att.attnum > 0 and not att.attisdropped |
| 34 | + group by 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, tbl.relhasoids |
| 35 | + order by 2, 3 |
| 36 | +), step2 as ( |
| 37 | + select |
| 38 | + *, |
| 39 | + ( |
| 40 | + 4 + tpl_hdr_size + tpl_data_size + (2 * ma) |
| 41 | + - case when tpl_hdr_size % ma = 0 then ma else tpl_hdr_size % ma end |
| 42 | + - case when ceil(tpl_data_size)::int % ma = 0 then ma else ceil(tpl_data_size)::int % ma end |
| 43 | + ) as tpl_size, |
| 44 | + bs - page_hdr as size_per_block, |
| 45 | + (heappages + toastpages) as tblpages |
| 46 | + from step1 |
| 47 | +), step3 as ( |
| 48 | + select |
| 49 | + *, |
| 50 | + ceil(reltuples / ((bs - page_hdr) / tpl_size)) + ceil(toasttuples / 4) as est_tblpages, |
| 51 | + ceil(reltuples / ((bs - page_hdr) * fillfactor / (tpl_size * 100))) + ceil(toasttuples / 4) as est_tblpages_ff |
| 52 | + -- , stattuple.pgstattuple(tblid) as pst |
| 53 | + from step2 |
| 54 | +), step4 as ( |
| 55 | + select |
| 56 | + *, |
| 57 | + tblpages * bs as real_size, |
| 58 | + (tblpages - est_tblpages) * bs as extra_size, |
| 59 | + case when tblpages - est_tblpages > 0 then 100 * (tblpages - est_tblpages) / tblpages::float else 0 end as extra_ratio, |
| 60 | + (tblpages - est_tblpages_ff) * bs as bloat_size, |
| 61 | + case when tblpages - est_tblpages_ff > 0 then 100 * (tblpages - est_tblpages_ff) / tblpages::float else 0 end as bloat_ratio |
| 62 | + -- , (pst).free_percent + (pst).dead_tuple_percent as real_frag |
| 63 | + from step3 |
61 | 64 | -- WHERE NOT is_na |
62 | 65 | -- AND tblpages*((pst).free_percent + (pst).dead_tuple_percent)::float4/100 >= 1 |
63 | 66 | ) |
64 | | -select current_database, schemaname, tblname, |
65 | | - real_size, pg_size_pretty(real_size::numeric) as real_size_pretty, |
66 | | - extra_size, pg_size_pretty(extra_size::numeric) as extra_size_pretty, |
67 | | - extra_ratio as "extra_ratio, %", |
68 | | - bloat_size, pg_size_pretty(bloat_size::numeric) as bloat_size_pretty, |
69 | | - bloat_ratio as "bloat_ratio, %", |
70 | | - fillfactor, |
71 | | - is_na, |
72 | | - real_size - bloat_size as live_data_size |
73 | | -from data |
74 | | -order by bloat_size desc |
| 67 | +select |
| 68 | + case is_na when true then 'TRUE' else '' end as is_na, |
| 69 | + coalesce(nullif(schema_name, 'public') || '.', '') || table_name as table, |
| 70 | + pg_size_pretty(real_size::numeric) as size, |
| 71 | + pg_size_pretty(extra_size::numeric)::text || ' (' || round(extra_ratio::numeric, 2)::text || '%)' as extra_estimated, |
| 72 | + pg_size_pretty(bloat_size::numeric)::text || ' (' || round(bloat_ratio::numeric, 2)::text || '%)' as bloat_estimated, |
| 73 | + fillfactor, |
| 74 | + pg_size_pretty((real_size - bloat_size)::numeric) as live |
| 75 | +\if :postgresdba_extended |
| 76 | + , |
| 77 | + real_size as real_size_raw, |
| 78 | + extra_size as extra_size_raw, |
| 79 | + bloat_size as bloat_size_raw, |
| 80 | + real_size - bloat_size as live_data_size_raw |
| 81 | +\endif |
| 82 | +from step4 |
| 83 | +order by real_size desc nulls last |
75 | 84 | ; |
76 | 85 |
|
| 86 | +/* |
| 87 | +Author of the original version: |
| 88 | + 2015, Jehan-Guillaume (ioguix) de Rorthais |
| 89 | +
|
| 90 | +License of the original version: |
| 91 | +
|
| 92 | +Redistribution and use in source and binary forms, with or without |
| 93 | +modification, are permitted provided that the following conditions are met: |
| 94 | +
|
| 95 | +* Redistributions of source code must retain the above copyright notice, this |
| 96 | + list of conditions and the following disclaimer. |
| 97 | +
|
| 98 | +* Redistributions in binary form must reproduce the above copyright notice, |
| 99 | + this list of conditions and the following disclaimer in the documentation |
| 100 | + and/or other materials provided with the distribution. |
| 101 | +
|
| 102 | +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 103 | +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 104 | +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 105 | +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| 106 | +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 107 | +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| 108 | +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| 109 | +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| 110 | +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 111 | +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 112 | +*/ |
0 commit comments