一、简单概述
在 DM 数据库中,表空间由一个或者多个数据文件组成。DM 数据库中的所有对象在逻辑上都存放在表空间中,而物理上都存储在所属表空间的数据文件中。
在创建 DM 数据库时,会自动创建 5 个表空间:SYSTEM 表空间、ROLL 表空间、MAIN 表空间、TEMP 表空间和 HMAIN 表空间。
SYSTEM 表空间存放了有关 DM 数据库的字典信息,用户不能在 SYSTEM 表空间创建表和索引。
ROLL 表空间完全由 DM 数据库自动维护,用户无需干预。该表空间用来存放事务运行过程中执行 DML 操作之前的值,从而为访问该表的其他用户提供表数据的读一致性视图。
MAIN 表空间在初始化库的时候,就会自动创建一个大小为 128M 的数据文件MAIN.DBF。在创建用户时,如果没有指定默认表空间,则系统自动指定 MAIN 表空间为用户默认的表空间。
TEMP 表空间完全由 DM 数据库自动维护。当用户的 SQL 语句需要磁盘空间来完成某个操作时,DM 数据库会从 TEMP 表空间分配临时段。如创建索引、无法在内存中完成的排序操作、SQL 语句中间结果集以及用户创建的临时表等都会使用到 TEMP 表空间。
HMAIN 表空间属于 HTS 表空间,完全由 DM 数据库自动维护,用户无需干涉。当用户在创建 HUGE 表时,未指定 HTS 表空间的情况下,充当默认 HTS 表空间。
每一个用户都有一个默认的表空间。对于 SYS、SYSSSO、SYSAUDITOR 系统用户,默认的用户表空间是 SYSTEM,SYSDBA 的默认表空间为 MAIN,新创建的用户如果没有指定默认表空间,则系统自动指定 MAIN 表空间为用户默认的表空间。如果用户在创建表的时候,指定了存储表空间 A,并且和当前用户的默认表空间 B 不一致时,表存储在用户指定的表空间 A 中,并且默认情况下,在这张表上面建立的索引也将存储在 A 中,但是用户的默认表空间是不变的,仍为 B。
一般情况下,建议用户自己创建一个表空间来存放业务数据,或者将数据存放在默认的用户表空间 MAIN 中。
二、相关SQL
(1)创建表空间
数据文件的最小值不能低于页大小的 4096 倍,最大值是页大小乘以 2 的 31 次方-1(如果页大小为 8K,最大值为 16T)
| 数据库页大小 | 每个字符类型字段实际最大长度(字节) | 每行记录最大字段外其他字段总长度(字节) | 表空间单个数据文件最小 (MB)/最大 (MB) |
| 4 KB | 1938 | 2047 | 16/8388608 |
| 8 KB | 3878 | 4095 | 32/16777216 |
| 16 KB | 8000 | 8195 | 64/33554432 |
| 32 KB | 8188 | 16176 | 128/67108864 |
CREATE TABLESPACE "TBS" DATAFILE '/dmdata/PROD/TBS01.DBF' SIZE 32 AUTOEXTEND ON NEXT 1 MAXSIZE 1024;
(2)修改表空间文件大小
ALTER TABLESPACE "TBS" RESIZE DATAFILE '/dmdata/PROD/TBS01.DBF' TO 64;
(3)添加数据文件
ALTER TABLESPACE "TBS" ADD DATAFILE '/dmdata/PROD/TBS02.DBF' SIZE 64 AUTOEXTEND ON NEXT 1 MAXSIZE 1024;
(4)修改数据文件扩展尺寸
ALTER TABLESPACE "TBS" DATAFILE '/dmdata/PROD/TBS01.DBF' AUTOEXTEND ON NEXT 3 MAXSIZE 1024;
--相关系统视图字段:DBA_DATA_FILES.INCREMENT_BY
(5)数据文件的迁移
ALTER TABLESPACE "TBS" OFFLINE;
ALTER TABLESPACE "TBS" RENAME DATAFILE '/dmdata/PROD/TBS01.DBF' TO '/dmdata/TBS01.DBF';
ALTER TABLESPACE "TBS" RENAME DATAFILE '/dmdata/PROD/TBS02.DBF' TO '/dmdata/TBS02.DBF';
(6)查询表空间使用率
--V$DATAFILE 和 V$TABLESPACE
SELECT
TS.NAME 表空间名字,
DF.TOTAL_SIZE * DF.PAGE_SIZE / 1024 / 1024 总大小M,
(DF.TOTAL_SIZE -DF.FREE_SIZE) * DF.PAGE_SIZE / 1024 / 1024 当前使用大小M,
DF.FREE_SIZE * DF.PAGE_SIZE / 1024 / 1024 剩余大小M,
ROUND((1 -DF.FREE_SIZE / DF.TOTAL_SIZE) * 100, 2) || '%' 使用百分比,
DF.MAX_SIZE 上限大小M,
DF.PATH 表空间文件路径,
DF.CLIENT_PATH 表空间客户端路径
FROM
V$DATAFILE DF
JOIN V$TABLESPACE TS ON DF.GROUP_ID = TS.ID
ORDER BY
TS.NAME,
DF.PATH;
--V$TABLESPACE
SELECT
NAME AS "名称",
(TOTAL_SIZE * PAGE) / (1024 * 1024) AS "大小(MB)",
(USED_SIZE * PAGE) / (1024 * 1024) AS "使用大小(MB)",
((TOTAL_SIZE - USED_SIZE) * PAGE) / (1024 * 1024) AS "空闲大小(MB)",
ROUND((USED_SIZE / TOTAL_SIZE), 4) * 100 AS "使用率",
(TOTAL_SIZE * PAGE) / (1024 * 1024 * 1024) AS "大小(GB)",
(USED_SIZE * PAGE) / (1024 * 1024 * 1024) AS "使用大小(GB)",
((TOTAL_SIZE - USED_SIZE) * PAGE) / (1024 * 1024 * 1024) AS "空闲大小(GB)",
ROUND((USED_SIZE / TOTAL_SIZE), 4) * 100 AS "使用率"
FROM
V$TABLESPACE;
--DBA_FREE_SPACE 和 DBA_DATA_FILES
SELECT
A.TABLESPACE_NAME AS "名称",
TOTAL / (1024 * 1024) AS "大小(M)",
(TOTAL - FREE) / (1024 * 1024) AS "使用大小(M)",
FREE / (1024 * 1024) AS "空闲大小(M)",
TOTAL / (1024 * 1024 * 1024) AS "大小(G)",
(TOTAL - FREE) / (1024 * 1024 * 1024) AS "使用大小(G)",
FREE / (1024 * 1024 * 1024) AS "空闲大小(G)",
ROUND((TOTAL - FREE) / TOTAL, 4) * 100 AS "使用率 %"
FROM
(SELECT TABLESPACE_NAME, SUM(BYTES) FREE FROM DBA_FREE_SPACE GROUP BY TABLESPACE_NAME) A,
(SELECT TABLESPACE_NAME, SUM(BYTES) TOTAL FROM DBA_DATA_FILES GROUP BY TABLESPACE_NAME) B
WHERE
A.TABLESPACE_NAME = B.TABLESPACE_NAME;
--V$TABLESPACE 和 V$DATA_FILE
SELECT
C.NAME 表空间名字,
D.TOTAL 总大小M,
D.USE 当前使用大小M,
D.FREE 剩余大小M,
ROUND(D.USED_PER, 2) || '%' 使用百分比,
E.MAX_SIZE 上限大小M
FROM
V$TABLESPACE C
JOIN (SELECT
A.ID,
SUM(B.TOTAL_SIZE) * B.PAGE_SIZE / 1024 / 1024 TOTAL,
SUM(B.FREE_SIZE) * B.PAGE_SIZE / 1024 / 1024 FREE,
SUM(B.TOTAL_SIZE * B.PAGE_SIZE / 1024 / 1024 - B.FREE_SIZE * B.PAGE_SIZE / 1024 / 1024) USE,
100 - (SUM(B.FREE_SIZE) * 100 / SUM(B.TOTAL_SIZE)) USED_PER
FROM
V$TABLESPACE A,
V$DATAFILE B
WHERE
A.ID = B.GROUP_ID
GROUP BY
A.ID,
B.PAGE_SIZE) D ON C.ID = D.ID
LEFT JOIN V$DATAFILE E ON C.ID = E.GROUP_ID
ORDER BY
ROUND(D.USED_PER, 2) DESC;
--开启自增,将自增空间计算在内
SELECT * FROM
(SELECT
T.NAME,
G.TOTAL_MB,
G.USED_MB,
G.FREE_MB,
G.USED_PERCENT,
CASE
WHEN G.USED_PERCENT <= 85 OR G.FREE_MB / 1024 > 60 THEN
'NORMAL'
WHEN G.USED_PERCENT > 85 AND G.USED_PERCENT <= 90 THEN
'WARNING'
WHEN G.USED_PERCENT > 90 THEN
'ERROR'
END INSPECTION_RESULT
FROM
(SELECT
GDF.GROUP_ID,
SUM(GDF.FILE_SIZE_MB) TOTAL_MB,
SUM(FILE_USED_MB) USED_MB,
SUM(GDF.FILE_SIZE_MB) - SUM(GDF.FILE_USED_MB) FREE_MB,
TO_CHAR(ROUND(SUM(FILE_USED_MB) / SUM(GDF.FILE_SIZE_MB) * 100, 2), '990.99') USED_PERCENT
FROM
(SELECT
DF.GROUP_ID,
round(
CASE
WHEN DF.AUTO_EXTEND = 1 THEN
DF.MAX_SIZE
ELSE
DF.TOTAL_SIZE * DF.PAGE_SIZE / 1024 / 1024
END, 1) FILE_SIZE_MB,
round((DF.TOTAL_SIZE -DF.FREE_SIZE) * DF.PAGE_SIZE / 1024 / 1024, 1) FILE_USED_MB
FROM
V$DATAFILE DF) GDF
GROUP BY
GDF.GROUP_ID) G,
V$TABLESPACE T
WHERE
G.GROUP_ID = T.ID
ORDER BY
5 DESC)
WHERE
ROWNUM <= 40;
(7)批量关闭表空间文件自动扩展
结果集生成SQL
SELECT 'ALTER TABLESPACE ' ||TABLESPACE_NAME|| ' DATAFILE ''' ||FILE_NAME|| ''''||' AUTOEXTEND OFF;' AS SQL
FROM DBA_DATA_FILES ORDER BY TABLESPACE_NAME, FILE_NAME;
执行结果集SQL,例如:
ALTER TABLESPACE TBS1 DATAFILE '/dmdata/5236/DMDB/TBS101.DBF' AUTOEXTEND OFF;
ALTER TABLESPACE TBS2 DATAFILE '/dmdata/5236/DMDB/TBS201.DBF' AUTOEXTEND OFF;
ALTER TABLESPACE IDX_TBS1 DATAFILE '/dmdata/5236/DMDB/IDX_TBS101.DBF' AUTOEXTEND OFF;
ALTER TABLESPACE IDX_TBS2 DATAFILE '/dmdata/5236/DMDB/IDX_TBS201.DBF' AUTOEXTEND OFF;
(8)修改表空间扩展上限
ALTER TABLESPACE "表空间名" DATAFILE '表空间文件绝对路径' AUTOEXTEND ON NEXT 100 MAXSIZE 512000;
(9)查询表空间扩展尺寸
SELECT
FILE_NAME,
TABLESPACE_NAME,
AUTOEXTENSIBLE AS "是否开启自动扩展",
INCREMENT_BY AS "每次扩展大小(MB)"
FROM
DBA_DATA_FILES;
(10)表空间数据文件中各表数据页位置分布
DECLARE
V_TABLESPACE_NAME VARCHAR2(128) := 'PMSNB%'; --要查询的表空间名,可以使用通配符%,不区分大小写
V_TABLE_NAME VARCHAR2(128) := '%'; --要查询的表名,可以使用通配符%,不区分大小写
---------------------------------------------------
TYPE TR_DATAPOS IS RECORD
(
TS_ID INT,
TS_NAME VARCHAR2(128),
FL_ID INT,
FL_PATH VARCHAR2(1000),
USER_ID INT,
USER_NAME VARCHAR2(128),
SCHEMA_ID INT,
SCHEMA_NAME VARCHAR2(128),
TABLE_ID INT,
TABLE_NAME VARCHAR2(128),
TAB_SIZE NUMBER(18,0),
INDEX_ID INT,
INDEX_NAME VARCHAR2(128),
INDEX_SIZE NUMBER(18,0),
INDEX_INFO VARCHAR2(400),
DIAGRAM VARCHAR2(600),
LOB_SIZE NUMBER(18,0),
SEG_ID INT,
SEG_TYPE VARCHAR2(128),
SEG_SIZE NUMBER(18,0),
EXTENTS INT,
EXTENT_SIZE NUMBER(18,0),
PAGE_COUNT INT,
MIN_PAGE_NO INT,
MIN_PAGE_POS_MB NUMBER(18,2),
MAX_PAGE_NO INT,
MAX_PAGE_POS_MB NUMBER(18,2)
);
TYPE TT_DATAPOS IS TABLE OF TR_DATAPOS;
V_DATAPOS TT_DATAPOS := TT_DATAPOS();
TYPE TR_SEGS IS RECORD
(
SEG_ID INT,
SEG_TYPE VARCHAR2(128)
);
TYPE TT_SEGS IS TABLE OF TR_SEGS;
V_SEGS TT_SEGS := TT_SEGS();
TYPE TR_FLS IS RECORD
(
FL_ID INT,
FILEPATH VARCHAR2(1000),
TOTAL_SIZE BIGINT,
MIN_PAGE_NO INT,
MIN_PAGE_POS_MB NUMBER(18,2),
MAX_PAGE_NO INT,
MAX_PAGE_POS_MB NUMBER(18,2),
PAGE_COUNT INT,
SEG_SIZE NUMBER(18,2),
EXTENTS INT
);
TYPE TT_FLS IS TABLE OF TR_FLS;
V_FLS TT_FLS := TT_FLS();
V_ROW INT := 0;
V_DIAGRAM VARCHAR2(600);
V_PREV_IDX_ID INT := 0;
V_SEG_ID INT;
FUNCTION BINARY_TO_VB(PBIN VARCHAR2)
RETURN VARBINARY(16) DETERMINISTIC
IS
V_LEN INT := LENGTH (PBIN);
V_NUM SMALLINT := 0;
V_LOOP INT := V_LEN / 8;
V_RET VARBINARY(32767);
BEGIN
FOR I IN 1 .. V_LOOP LOOP
V_NUM := 0;
FOR J IN 1..8 LOOP
IF SUBSTR(PBIN,(I - 1) * 8 + J, 1) = '1' THEN
V_NUM := V_NUM + POWER(2, 8 - J);
END IF;
END LOOP;
V_RET := V_RET || SUBSTRBB(CAST(V_NUM AS VARBINARY),2,1);
END LOOP;
RETURN V_RET;
END BINARY_TO_VB;
FUNCTION VB_TO_BINARY(V_DATA VARBINARY(16))
RETURN VARCHAR DETERMINISTIC
IS
V_NUM SMALLINT;
V_LEN INT;
V_RET VARCHAR2(1000);
V_BYTE VARCHAR2(100);
BEGIN
V_LEN := LENGTHB(V_DATA);
FOR I IN 1 .. V_LEN LOOP
V_NUM := CAST(SUBSTRBB(V_DATA,I,1) AS SMALLINT);
V_BYTE := '';
FOR J IN 1 .. 8 LOOP
V_BYTE := MOD(V_NUM,2)||V_BYTE;
V_NUM := TRUNC(V_NUM / 2);
END LOOP;
V_RET := V_RET || V_BYTE;
END LOOP;
RETURN V_RET;
END VB_TO_BINARY;
BEGIN
FOR C1 IN (SELECT TS.ID AS TS_ID
,TS.NAME AS TS_NAME
,USR.ID AS USER_ID
,USR.NAME AS USER_NAME
,SCH.ID AS SCHEMA_ID
,SCH.NAME AS SCHEMA_NAME
,TAB.ID AS TABLE_ID
,TAB.NAME AS TABLE_NAME
,CAST(TABLE_USED_SPACE(SCH.NAME,TAB.NAME) AS NUMBER) * PAGE AS TAB_SIZE
,IDX.ID AS INDEX_ID
,IDX.NAME AS INDEX_NAME
,CAST(INDEX_USED_SPACE(IDX.ID) AS NUMBER) * PAGE AS INDEX_SIZE
,RTRIM( DECODE(IDXINFO.FLAG & 0x01,0x00,'正常索引;')
||DECODE(IDXINFO.FLAG & 0x01,0x01,'系统索引;')
||DECODE(IDXINFO.FLAG & 0x02,0x02,'虚索引(VIRTUAL);')
||DECODE(IDXINFO.FLAG & 0x04,0x04,'主键;')
||DECODE(IDXINFO.FLAG & 0x08,0x08,'在临时表上;')
||DECODE(IDXINFO.FLAG & 0x10,0x10,'无效索引;')
||DECODE(IDXINFO.FLAG & 0x20,0x20,'fast pool;')
||DECODE(IDXINFO.XTYPE & 0x01,0x00,CASE WHEN (TAB.INFO3 & 0x3F) IN (0x13, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27) THEN 'HUGE表聚集索引(HUGE CLUSTER);' ELSE '聚集索引(CLUSTER);' END)
||DECODE(IDXINFO.XTYPE & 0x01,0x01,'二级索引;')
||DECODE(IDXINFO.XTYPE & 0x02,0x02,'函数索引(FUNCTION-BASED NORMAL);')
||DECODE(IDXINFO.XTYPE & 0x04,0x04,'全局索引在水平分区子表上;')
||DECODE(IDXINFO.XTYPE & 0x08,0x08,'全局索引在水平分区主表上;')
||DECODE(IDXINFO.XTYPE & 0x10,0x10,'唯一索引;')
||DECODE(IDXINFO.XTYPE & 0x20,0x20,'扁平索引(FLAT);')
||DECODE(IDXINFO.XTYPE & 0x40,0x40,'数组索引;')
||DECODE(IDXINFO.XTYPE & 0x800,0x800,'该位图索引是由改造后创建;')
||DECODE(IDXINFO.XTYPE & 0x1000,0x1000,'位图索引(BITMAP);')
||DECODE(IDXINFO.XTYPE & 0x2000,0x2000,'位图连接索引(BITMAP);')
||DECODE(IDXINFO.XTYPE & 0x4000,0x4000,'位图连接索引虚索引(BITMAP);')
||DECODE(IDXINFO.XTYPE & 0x8000,0x8000,'空间索引;')
||DECODE(IDXINFO.XTYPE & 0x10000,0x00,'','索引不可见;')
||DECODE(IDXINFO.XTYPE & 0x20000,0x20000,'标识 DPC全局索引的根索引;')
||DECODE(IDXINFO.XTYPE & 0x40000,0x40000,'标识 DPC全局索引的子索引;')
,';') AS INDEX_INFO
,CASE WHEN IDXINFO.XTYPE & 0x01 = 0x00 THEN 1 ELSE 0 END AS IS_CLUSTER
,CAST(TABLE_USED_LOB_PAGES(SCH.NAME,TAB.NAME) AS NUMBER) * PAGE AS LOB_SIZE
FROM SYSOBJECTS IDX
,SYSINDEXES IDXINFO
,SYSOBJECTS TAB
,SYSOBJECTS SCH
,SYSOBJECTS USR
,V$TABLESPACE TS
WHERE IDX.ID = IDXINFO.ID
AND TAB.ID = IDX.PID
AND SCH.ID = TAB.SCHID
AND USR.ID = SCH.PID
AND USR.TYPE$ = 'UR'
AND IDXINFO.GROUPID = TS.ID
AND UPPER(TS.NAME) LIKE UPPER(V_TABLESPACE_NAME)
AND UPPER(TAB.NAME) LIKE UPPER(V_TABLE_NAME)
AND TS.STATUS$ = 0
ORDER BY SCHEMA_NAME,TABLE_NAME,INDEX_ID
) LOOP
V_SEGS.DELETE;
SELECT SEG_ID,TYPE AS SEG_TYPE
BULK COLLECT INTO V_SEGS
FROM V$SEGMENT_INFOS
WHERE OBJ_ID = C1.INDEX_ID
AND TS_ID = C1.TS_ID
GROUP BY SEG_ID,TYPE
ORDER BY SEG_ID;
IF C1.IS_CLUSTER = 1 THEN
--在某些版本或环境中,V$LOB_SEG 查询无结果时会抛异常,所以只能用循环加异常捕获方式来处理
BEGIN
V_SEG_ID := NULL;
SELECT SEG_ID
INTO V_SEG_ID
FROM V$LOB_SEG
WHERE TAB_ID = C1.TABLE_ID;
IF V_SEG_ID IS NOT NULL THEN
V_SEGS.EXTEND;
V_SEGS(V_SEGS.COUNT).SEG_ID := V_SEG_ID;
V_SEGS(V_SEGS.COUNT).SEG_TYPE := 'LOB_SEG';
END IF;
EXCEPTION WHEN OTHERS THEN
NULL;
END;
END IF;
FOR I_SEG IN 1 .. V_SEGS.COUNT LOOP
V_FLS.DELETE;
--在某些版本或环境中,V$SEGMENT_PAGES 查询无结果时会抛异常,所以只能用循环加异常捕获方式来处理
BEGIN
SELECT FL.ID,FL.PATH AS FILEPATH,FL.TOTAL_SIZE
,PGS.MIN_PAGE_NO
,ROUND(CAST(PGS.MIN_PAGE_NO AS NUMBER) * FL.PAGE_SIZE / 1024 / 1024,2) AS MAX_PAGE_POS_MB
,PGS.MAX_PAGE_NO
,ROUND(CAST(PGS.MAX_PAGE_NO AS NUMBER) * FL.PAGE_SIZE / 1024 / 1024,2) AS MAX_PAGE_POS_MB
,PGS.PAGE_COUNT
,CAST(PGS.PAGE_COUNT AS NUMBER) * FL.PAGE_SIZE AS SEG_SIZE
,NVL(EXTS.EXTENTS,0) AS EXTENTS
BULK COLLECT INTO V_FLS
FROM V$DATAFILE FL
JOIN (SELECT FILE_ID
,MIN(PAGE_NO) AS MIN_PAGE_NO
,MAX(PAGE_NO) AS MAX_PAGE_NO
,COUNT(1) AS PAGE_COUNT
FROM V$SEGMENT_PAGES
WHERE GROUP_ID = C1.TS_ID
AND SEG_ID = V_SEGS(I_SEG).SEG_ID
GROUP BY FILE_ID
) PGS
ON PGS.FILE_ID = FL.ID
LEFT JOIN (SELECT FILE_ID,COUNT(1) AS EXTENTS
FROM V$EXTENTS
WHERE TS_ID = C1.TS_ID
AND SEG_ID = V_SEGS(I_SEG).SEG_ID
GROUP BY FILE_ID
) EXTS
ON EXTS.FILE_ID = FL.ID
WHERE FL.GROUP_ID = C1.TS_ID;
EXCEPTION WHEN OTHERS THEN
NULL;
END;
FOR I_FL IN 1 .. V_FLS.COUNT LOOP
SELECT '['||LISTAGG(NVL2(B.PCT,'▓',' ')) WITHIN GROUP (ORDER BY A.PCT)||']'
INTO V_DIAGRAM
FROM (SELECT LEVEL AS PCT CONNECT BY LEVEL <= 100) A
LEFT JOIN (SELECT FILE_ID,CEIL(CAST(PAGE_NO AS NUMBER) / V_FLS(I_FL).TOTAL_SIZE * 100) AS PCT
FROM V$SEGMENT_PAGES
WHERE GROUP_ID = C1.TS_ID
AND SEG_ID = V_SEGS(I_SEG).SEG_ID
GROUP BY FILE_ID,CEIL(CAST(PAGE_NO AS NUMBER) / V_FLS(I_FL).TOTAL_SIZE * 100)
HAVING FILE_ID = V_FLS(I_FL).FL_ID
) B
ON B.PCT = A.PCT;
V_DATAPOS.EXTEND;
V_ROW := V_DATAPOS.COUNT;
V_DATAPOS(V_ROW).TS_ID = C1.TS_ID;
V_DATAPOS(V_ROW).TS_NAME = C1.TS_NAME;
V_DATAPOS(V_ROW).FL_ID = V_FLS(I_FL).FL_ID;
V_DATAPOS(V_ROW).FL_PATH = V_FLS(I_FL).FILEPATH;
V_DATAPOS(V_ROW).USER_ID = C1.USER_ID;
V_DATAPOS(V_ROW).USER_NAME = C1.USER_NAME;
V_DATAPOS(V_ROW).SCHEMA_ID = C1.SCHEMA_ID;
V_DATAPOS(V_ROW).SCHEMA_NAME = C1.SCHEMA_NAME;
V_DATAPOS(V_ROW).TABLE_ID = C1.TABLE_ID;
V_DATAPOS(V_ROW).TABLE_NAME = C1.TABLE_NAME;
V_DATAPOS(V_ROW).TAB_SIZE = C1.TAB_SIZE;
IF V_SEGS(I_SEG).SEG_TYPE = 'LOB_SEG' THEN
V_DATAPOS(V_ROW).LOB_SIZE = C1.LOB_SIZE;
V_DATAPOS(V_ROW).INDEX_INFO = 'LOB数据';
ELSE
V_DATAPOS(V_ROW).INDEX_ID = C1.INDEX_ID;
V_DATAPOS(V_ROW).INDEX_NAME = C1.INDEX_NAME;
IF V_PREV_IDX_ID <> C1.INDEX_ID THEN
V_PREV_IDX_ID := C1.INDEX_ID;
V_DATAPOS(V_ROW).INDEX_SIZE = C1.INDEX_SIZE;
END IF;
V_DATAPOS(V_ROW).INDEX_INFO = C1.INDEX_INFO;
END IF;
V_DATAPOS(V_ROW).SEG_ID = V_SEGS(I_SEG).SEG_ID;
V_DATAPOS(V_ROW).SEG_TYPE = V_SEGS(I_SEG).SEG_TYPE;
V_DATAPOS(V_ROW).SEG_SIZE = V_FLS(I_FL).SEG_SIZE;
V_DATAPOS(V_ROW).PAGE_COUNT = V_FLS(I_FL).PAGE_COUNT;
V_DATAPOS(V_ROW).EXTENTS = V_FLS(I_FL).EXTENTS;--= V_SEGS(I_SEG).EXTENTS;
V_DATAPOS(V_ROW).EXTENT_SIZE = CAST(V_FLS(I_FL).EXTENTS AS NUMBER) * SF_GET_EXTENT_SIZE * PAGE;--CAST(V_SEGS(I_SEG).EXTENTS AS NUMBER) * SF_GET_EXTENT_SIZE * PAGE;
V_DATAPOS(V_ROW).MIN_PAGE_NO = V_FLS(I_FL).MIN_PAGE_NO;
V_DATAPOS(V_ROW).MIN_PAGE_POS_MB = V_FLS(I_FL).MIN_PAGE_POS_MB;
V_DATAPOS(V_ROW).MAX_PAGE_NO = V_FLS(I_FL).MAX_PAGE_NO;
V_DATAPOS(V_ROW).MAX_PAGE_POS_MB = V_FLS(I_FL).MAX_PAGE_POS_MB;
V_DATAPOS(V_ROW).DIAGRAM = V_DIAGRAM;
END LOOP;
END LOOP;
END LOOP;
--列出指定表空间数据文件使用情况
SELECT TS.NAME AS TABLESPACE_NAME
,DF.PATH AS FILE_PATH
,ROUND(CAST(DF.TOTAL_SIZE * PAGE AS NUMBER) / 1024.0 / 1024.0,2) AS TOTAL_SIZE_MB
,ROUND(CAST((DF.TOTAL_SIZE - DF.FREE_SIZE) * PAGE AS NUMBER) / 1024.0 / 1024.0,2) AS USED_SIZE_MB
,ROUND(CAST(DF.FREE_SIZE * PAGE AS NUMBER) / 1024.0 / 1024.0,2) AS FREE_SIZE_MB
,CEIL(CAST(DF.FREE_PAGE_NO * PAGE AS NUMBER) / 1024.0 / 1024.0) AS LEAST_FILE_SIZE_MB
--250423版本之后可用
--,DT.DIAGRAM AS "DIAGRAM(with pred)"
,DT.USED_SIZE_MB AS "USED_SIZE_MB(with pred)"
,DT.MIN_PAGE_POSITION_MB
,DT.MAX_PAGE_POSITION_MB
FROM (SELECT TS_ID
,FL_ID
--250423版本之后可用
--,'['||REPLACE(REPLACE(SUBSTR(VB_TO_BINARY(BIT_OR(BINARY_TO_VB(REPLACE(REPLACE(SUBSTR(DIAGRAM,2,100),'▓','1'),' ','0')||'0000'))),1,100),'0',' '),'1','▓')||']' AS DIAGRAM
,ROUND(CAST(SUM(EXTENT_SIZE) AS NUMBER) / 1024 / 1024,2) AS USED_SIZE_MB
,MIN(MIN_PAGE_POS_MB) AS MIN_PAGE_POSITION_MB
,MAX(MAX_PAGE_POS_MB) AS MAX_PAGE_POSITION_MB
FROM TABLE(V_DATAPOS)
GROUP BY TS_ID,FL_ID
) DT
,V$DATAFILE DF
,V$TABLESPACE TS
WHERE DF.GROUP_ID = DT.TS_ID
AND DF.ID = DT.FL_ID
AND TS.ID = DT.TS_ID
ORDER BY TS.NAME,DF.PATH,MAX_PAGE_POSITION_MB DESC;
--列出指定表空间内,指定表名范围的各表数据页在数据文件里的分布情况
SELECT TS_NAME
,FL_PATH
,SCHEMA_NAME
,TABLE_NAME
--250423版本之后可用
--,'['||REPLACE(REPLACE(SUBSTR(VB_TO_BINARY(BIT_OR(BINARY_TO_VB(REPLACE(REPLACE(SUBSTR(DIAGRAM,2,100),'▓','1'),' ','0')||'0000'))),1,100),'0',' '),'1','▓')||']' AS DIAGRAM
,ROUND(CAST(SUM(EXTENT_SIZE) AS NUMBER) / 1024 / 1024,2) AS USED_SIZE_MB
,MIN(MIN_PAGE_POS_MB) AS MIN_PAGE_POSITION_MB
,MAX(MAX_PAGE_POS_MB) AS MAX_PAGE_POSITION_MB
FROM TABLE(V_DATAPOS)
GROUP BY TS_NAME,FL_PATH,SCHEMA_NAME,TABLE_NAME
ORDER BY TS_NAME,FL_PATH,MAX_PAGE_POSITION_MB DESC;
--列出指定表空间内,指定表名范围的各表各种段的数据页分布情况
SELECT * FROM TABLE(V_DATAPOS) ORDER BY TS_ID,FL_ID,MAX_PAGE_POS_MB DESC;
END;
这个SQL文件里是一个脚本,非侵入性的,也就是不会在库里创建对象或修改数据,只是做查询。
注意,因为 V$LOB_SEG、V$SEGMENT_PAGES 等系统动态视图查询时会扫描数据页,可能会耗时比较长。
这个脚本前面我已经修改表空间名为你用的这个PMSNB,脚本执行后会列出三个结果集:
结果集1:表空间下各数据文件的存储内容占用位置情况
结果集2:表空间下各表相关数据页面在数据文件中的位置占用情况
结果集3:表空间下各表、各数据段在数据文件中的位置占用情况
先看看这个脚本是否能正常执行,如果能正常跑,就看一下输出内容,关键看这三个结果集最后的 MAX_PAGE_POS/POSITION_MB,也就是数据页面在数据文件中所处的绝对位置最大值。
因为达梦是使用类似Oracle的数据文件存储方式,而不是每个表单独文件存储,所以当某个数据页面占用了数据文件的尾部位置,就可能会引起收缩失败。通过查询结果集的位置最大值,能够分析出是哪张表以及是哪个索引占用了数据文件尾部位置。
如果处于尾部的表不多,且表不是很大的话,可以测试下用ALTER TABLE MOVE TABLESPACE的方法把这个表的数据先挪到其他表空间,等当前表空间收缩成功后再移动回来。
或者,实在不行,就用dexp/dimp工具,把数据导入到其他表空间里。
另附
表空间相关视图
- DBA_DATA_FILES
- DBA_TABLESPACES
- DBA_FREE_SPACE