根据数据页,填充因子,表对象行长度,计算每页大约存放行数
需要进一步验证
重新贴出sql语句,仅供参考,感觉和实际存储空间还有一定差值。
SELECT
'WITH page_info AS (' || CHR(10) ||
' SELECT TO_NUMBER(PARA_VALUE) AS page_size' || CHR(10) ||
' FROM v$dm_ini' || CHR(10) ||
' WHERE PARA_NAME = ''GLOBAL_PAGE_SIZE''' || CHR(10) ||
'),' || CHR(10) ||
'fillfactor_info AS (' || CHR(10) ||
' SELECT' || CHR(10) ||
' CASE TO_NUMBER(PARA_VALUE)' || CHR(10) ||
' WHEN 0 THEN 1' || CHR(10) ||
' ELSE TO_NUMBER(PARA_VALUE) / 100' || CHR(10) ||
' END AS fillfactor_ratio,' || CHR(10) ||
' CASE TO_NUMBER(PARA_VALUE)' || CHR(10) ||
' WHEN 0 THEN 100' || CHR(10) ||
' ELSE TO_NUMBER(PARA_VALUE)' || CHR(10) ||
' END AS fillfactor_pct' || CHR(10) ||
' FROM v$dm_ini' || CHR(10) ||
' WHERE PARA_NAME = ''DEFAULT_FILLFACTOR''' || CHR(10) ||
'),' || CHR(10) ||
'row_stats AS (' || CHR(10) ||
' SELECT' || CHR(10) ||
' COUNT() AS total_rows,' || CHR(10) ||
' (' || bb.v_sql2 || ' 0.00)' || CHR(10) ||
' / COUNT() AS avg_row_bytes' || CHR(10) ||
' FROM ' || bb.schemaname || '.' || bb.tablename || CHR(10) ||
')' || CHR(10) ||
'SELECT' || CHR(10) ||
' pi.page_size / 1024 AS page_size_kb,' || CHR(10) ||
' ROUND(rs.avg_row_bytes, 2) AS avg_row_bytes,' || CHR(10) ||
' fi.fillfactor_pct AS fillfactor_pct,' || CHR(10) ||
' ROUND((pi.page_size - 72) * fi.fillfactor_ratio, 2) AS usable_space,' || CHR(10) ||
' ROUND(rs.avg_row_bytes + 24, 2) AS row_total_overhead,' || CHR(10) ||
' FLOOR((pi.page_size - 72) * fi.fillfactor_ratio' || CHR(10) ||
' / (rs.avg_row_bytes + 24)) AS rows_per_page,' || CHR(10) ||
' rs.total_rows,' || CHR(10) ||
' ROUND(rs.total_rows * rs.avg_row_bytes / 1024 / 1024, 2) AS total_data_mb' || CHR(10) ||
'FROM page_info pi, fillfactor_info fi, row_stats rs;' AS generated_sql
FROM (
SELECT
aa.schemaname,
aa.tablename,
LISTAGG(aa.v_sql, '') WITHIN GROUP (ORDER BY aa.colname) AS v_sql2
FROM (
SELECT
c.name AS schemaname,
a.name AS colname,
b.name AS tablename,
'NVL(SUM(LENGTHB(' || a.name || ')), 0) + ' AS v_sql
FROM SYSCOLUMNS a
INNER JOIN SYSOBJECTS b ON a.id = b.id
AND b.subtype$ = 'UTAB'
AND b.name IN ('UPMS_PROCESS_FORM') -- ← 替换为目标表名
INNER JOIN SYSOBJECTS c ON b.schid = c.id
AND c.name = 'YCQ' -- ← 替换为目标模式名
AND c.type$ = 'SCH'
) aa
GROUP BY aa.schemaname, aa.tablename
) bb;
备注:
page_size_kb 数据页大小 = 32 KB(32768 字节),达梦最大值
avg_row_bytes 单行平均数据字节数(不含行头开销)
fillfactor_pct 填充因子 = 100%,即页空间全部可用,不预留更新空间
usable_space 每页扣除固定开销后可实际用于存数据的字节数
row_total_overhead 每行在页上的总占用(数据 + 行头 + 行目录)
rows_per_page 每个数据页平均可存储的行数
total_rows 表的总行数
total_data_mb 表中字段的总数据体积(MB)
看看能否换一个办法,也就是根据表中现有行数,除以表的簇集索引已经占用的数据页数,得出一个目前的平均每页记录数
不过这个最好能记录数多一些,数据页数量页多一些,太少的话误差比较大。
现在达梦创建的表会有1~2个辅助页,对平均值的计算结果(尤其是记录数比较少时)可能会有一定的影响。
试着写了个简单的查询,没有考虑LOB段,你测试下,看看是否符合预期。
DECLARE
V_SCHEMA_NAME VARCHAR2(128) := '模式名';
V_TABLE_NAME VARCHAR2(128) := '表名';
------------------------------------------
V_SEG_INFO DBMS_SPACE.SEG_T;
V_HAS_LEAF BOOLEAN := FALSE;
V_SEGID INT;
BEGIN
FOR C1 IN (SELECT SCH.NAME AS SCHEMA_NAME
,TAB.NAME AS TABLE_NAME
,IDX.NAME AS CLUSTER_INDEX_NAME
,TABLE_ROWCOUNT(SCH.NAME,TAB.NAME) AS ROW_CNT
,BITAND(TAB.INFO1,0xFF000000) >> 24 AS TAB_FILLFACTOR
,BITAND(IDX.INFO1,0x0FF) AS CLU_FILLFACTOR
,IDXINFO.GROUPID
,IDXINFO.ROOTFILE
,IDXINFO.ROOTPAGE
,IDXINFO.XTYPE
FROM SYSOBJECTS TAB
,SYSOBJECTS SCH
,SYSOBJECTS IDX
,SYSINDEXES IDXINFO
WHERE SCH.ID = TAB.SCHID
AND IDX.PID = TAB.ID
AND IDX.SUBTYPE$ = 'INDEX'
AND IDXINFO.ID = IDX.ID
AND BITAND(IDXINFO.XTYPE,0x01) = 0x00
AND SCH.NAME = V_SCHEMA_NAME
AND TAB.NAME = V_TABLE_NAME
) LOOP
V_HAS_LEAF := FALSE;
V_SEGID := 0;
IF BITAND(C1.XTYPE,0x20) <> 0x20 THEN --堆表没有LEAF段
--LEAF段
V_SEG_INFO := DBMS_SPACE.SEG_GET_BY_HEADER(C1.GROUPID,C1.ROOTFILE,C1.ROOTPAGE,62);
IF V_SEG_INFO.SEGID > 0 AND V_SEG_INFO.N_FULL_EXTENT + V_SEG_INFO.N_FRAG_EXTENT > 0 THEN
V_HAS_LEAF := TRUE;
V_SEGID := V_SEG_INFO.SEGID;
END IF;
END IF;
IF NOT V_HAS_LEAF THEN
--INNER段
V_SEG_INFO := DBMS_SPACE.SEG_GET_BY_HEADER(C1.GROUPID,C1.ROOTFILE,C1.ROOTPAGE,72);
IF V_SEG_INFO.SEGID > 0 AND V_SEG_INFO.N_FULL_EXTENT + V_SEG_INFO.N_FRAG_EXTENT > 0 THEN
V_SEGID := V_SEG_INFO.SEGID;
END IF;
END IF;
IF V_SEGID > 0 THEN
SELECT C1.SCHEMA_NAME
,C1.TABLE_NAME
,C1.CLUSTER_INDEX_NAME
,C1.TAB_FILLFACTOR
,C1.CLU_FILLFACTOR
,SEG_ID||'['||CASE WHEN V_HAS_LEAF THEN 'LEAF' ELSE 'INNER' END||']' AS SEG_ID
,C1.ROW_CNT
,SUM(USED) AS USED_PAGES
,SUM(CASE WHEN STATE = 'FRAG' THEN USED END) AS FRAG_PAGE_CNT
,SUM(CASE WHEN STATE = 'FULL' THEN USED END) AS FULL_PAGE_CNT
,CASE WHEN SUM(USED) = 0 THEN 0 ELSE FLOOR(CAST(C1.ROW_CNT AS NUMBER) / SUM(USED)) END AS AVG_ROWS
FROM V$EXTENTS
WHERE SEG_ID = V_SEGID
GROUP BY SEG_ID;
END IF;
END LOOP;
END;
想了想,觉得自己之前给的方案不符合题目,补充一下吧。
如果表已创建,可以参考上面我给出的第二个回帖中的方案。基于表中预先插入的测试数据,可以得到相对比较精准的页均记录数。
如果表未创建,则需要根据规划的表结构各字段类型和预期的平均数据长度,来估算一个记录长度,然后根据公式计算出可能的页均记录数。
我下面给出了一个大概的算法,你看看能否理解
其中
1 PAGE_TAIL_SIZE 参数需要到 V$DM_INI 中查询,其他预定占位宽度我给了个简单的名字,应该能猜出大概用途
2 我给出的CHAR类型宽度计算公式可能不准确,只能是估算使用
3 后面两个方案我拿不准,建议你分别查一下,并与实际存储记录数做比对,判断哪个更符合实际情况
4 对于开启 USING LONG ROW 的表,如果VARCHAR类型数据长度超过页面大小的一半或8192字节,好像最多是8188字节,可能会将数据写入到LOB段,这时在簇集索引中实际只有LOB说明的21字节内容
5 对于表上的LOB数据,是单独存储,这个先不算了。。。
#各列根据数据类型估算宽度
datasize =
{
fixedSize : [BIT,BOOL,BOOLEAN,BYTE : 1
,BINARY : LENGTH$
,TINYINT : 1
,SMALLINT : 2
,INT : 4
,INTEGER : 4
,BIGINT : 8
,DOUBLE : 8
,FLOAT : LENGTH$
,REAL,BINARY_FLOAT : 4
,DATE : 3
,TIME : 5
,TIME WITH TIME ZONE : 7
,TIMESTAMP : LENGTH$
,DATETIME : LENGTH$
,ROWID : 12
,DATETIME WITH TIME ZONE : LENGTH$
,TIMESTAMP WITH LOCAL TIME ZONE : LENGTH$
,CHAR( < 8) : LENGTH$
]
variSize : [VARBINARY,NCHAR,VARCHAR2,VARCHAR,NVARCHAR,NVARCHAR2 : (< 126 : 1,>= 127:2) + DATALENGTH
,CHAR(>=8) : (< 126 : 1,>= 127:2) + DATALENGTH
,NUMBER
,LOB 21
]
}
#估算行长度
rowsize = row_statedatasize(2) + CEIL(colcount / 4) + row_ctrldatasize(19) + datasize
#填充率如果为0,则是百分百,调整成小数
fillfactor = (fillfactor == 0?100:fillfactor) / 100
#估算函数1,固定开销不参与预留,fillfactor指:在扣除固定开销后的行数据区内部,实际用于插入数据行的比例:
rn = (PAGE - pageHeaderSize(0x62) - slotLinkHeadTailsize(4) - PAGE_TAIL_SIZE(from v$dm_ini)) * fillfactor / (rowsize + slotPosSize(2))
#估算函数2,固定开销也从预留空间中扣除,fillfactor指:整页中可用于存储的总空间比例:
rn = (PAGE * fillfactor - pageHeaderSize(0x62) - slotLinkHeadTailsize(4) - PAGE_TAIL_SIZE(from v$dm_ini)) / (rowsize + slotPosSize(2))

每页大约存放行数 ≈ (页大小 × 填充因子) ÷ 单行实际长度(向下取整),即把“数据页可用的存储空间”除以“每行占用的字节数”。