参考文档:DM 逻辑结构概述 | 达梦技术文档
数据类型
1、字符数据类型
系统参数:UNICODE_FLAG,0 代表 gb18030;1 代表 UTF-8。
配置 字段长度 英文存储个数 中文存储个数 实际字节数 备注
GB18030, LENGTH_IN_CHAR=0 VARCHAR(10) 10 5 10 中文占 2 字节
GB18030, LENGTH_IN_CHAR=1 VARCHAR(10) 20 10 20 中文站 2 字节,以字符为单位,自动扩充为双倍字节数
UTF-8, LENGTH_IN_CHAR=0 VARCHAR(10) 10 3.3 10 中文占 3 字节
UTF-8, LENGTH_IN_CHAR=1 VARCHAR(10) 40 13.3 40 中文站 3 字节,以字符为单位,自动扩充为 4 倍字节数
举例:UTF-8, LENGTH_IN_CHAR=0
create table users (id int,name varchar(10));
insert into users(id,name) values (1,'哈哈哈哈');提示超出长度
insert into users(id,name) values (1,'哈哈哈');成功
查询数据库的页大小
SELECT '页大小',cast(PAGE()/1024 as varchar);
2、数值数据类型
精确数值数据类型包括:NUMERIC、DECIMAL、DEC 类型、NUMBER 类型、INTEGER 类型、INT 类型、BIGINT 类型、TINYINT 类型、BYTE 类型、SMALLINT 类型、BINARY 类型、VARBINARY 类型。
近似数值类型包括:FLOAT 类型、DOUBLE 类型、REAL 类型、DOUBLE PRECISION 类型
举例:NUMERIC(4,1) 定义了小数点前面 3 位和小数点后面 1 位,共 4 位的数字,范围在 -999.9 到 999.9。
create table users (id int,qty numeric(4,1));
insert into users(id,qty) values (1,1000); 失败
insert into users(id,qty) values (1,999); 成功
3、位串数据类型
BIT:BIT 类型用于存储整数数据 1、0 或 NULL,只有 0 才转换为假,其他非空、非 0 值都会自动转换为真,可以用来支持 ODBC 和 JDBC 的布尔数据类型。DM 的 BIT 类型与 SQL Server 2000 的 BIT 数据类型相似。
举例:
create table users (id int,sex bit);
insert into users(id,sex) values (1,0);
insert into users(id,sex) values (2,1);
4、日期数据类型
日期时间数据类型分为一般日期时间数据类型、时间间隔数据类型和时区数据类型三类,用于存储日期、时间和它们之间的间隔信息。
4.1一般日期时间数据类型
DATE:年月日;TIME:时分秒;TIMESTAMP:年月日时分秒
举例:
CREATE TABLE users(
id INT,
pay_date DATE,
pay_time TIME(2),
pay_timestamp TIMESTAMP
);
INSERT INTO users values(1,'2020-11-19','23:59:59','2020-11-19 23:59:59');
注意:time和timestamp秒字段后面位数取值范围为0-6,默认为0
4.2时间间隔数据类型
年-月间隔类和日-时间隔类;前者结合年月,后者结合日时分秒;默认精度为6
| 类型名 | 类型描述 |
| INTERVAL YEAR(P) | 年间隔,即两个日期之间的年数字,P 为时间间隔的引导精度 |
| INTERVAL MONTH(P) | 月间隔,即两个日期之间的月数字,P 为时间间隔的引导精度 |
| INTERVAL DAY(P) | 日间隔,即为两个日期/时间之间的日数字,P 为时间间隔的引导精度 |
| INTERVAL HOUR(P) | 时间隔,即为两个日期/时间之间的时数字,P 为时间间隔的引导精度 |
| INTERVAL MINUTE(P) | 分间隔,即为两个日期/时间之间的分数字,P 为时间间隔的引导精度 |
| INTERVAL SECOND(P,Q) | 秒间隔,即为两个日期/时间之间的秒数字,P 为时间间隔的引导精度,Q 为时间间隔秒精度 |
| INTERVAL YEAR(P) TO MONTH | 年月间隔,即两个日期之间的年月数字,P 为时间间隔的引导精度 |
| INTERVAL DAY(P) TO HOUR | 日时间隔,即为两个日期/时间之间的日时数字,P 为时间间隔的引导精度 |
| INTERVAL DAY(P) TO MINUTE | 日时分间隔,即为两个日期/时间之间的日时分数字,P 为时间间隔的引导精度 |
| INTERVAL DAY(P)TO SECOND(Q) | 日时分秒间隔,即为两个日期/时间之间的日时分秒数字,P 为时间间隔的引导精度,Q 为时间间隔秒精度 |
| INTERVALL HOUR(P) TO MINUTE | 时分间隔,即为两个日期/时间之间的时分数字,P 为时间间隔的引导精度 |
| INTERVAL HOUR(P) TO SECOND(Q) | 时分秒间隔,即为两个日期/时间之间的时分秒数字,P 为时间间隔的引导精度,Q 为时间间隔秒精度 |
| INTERVAL MINUTE(P) TO SECOND(Q) | 分秒间隔,即为两个日期/时间之间的分秒间隔,P 为时间间隔的引导精度,Q 为时间间隔秒精度 |
5、多媒体数据类型
TEXT/LONG/LONGVARCHAR 类型:变长字符串类型。其字符串的长度最大为 100G-1,用于存储长的文本串。
IMAGE/LONGVARBINARY 类型:用于指明多媒体信息中的图像类型,长度最大为 100G-1 字节。
BLOB 类型:用于指明变长的二进制大对象,长度最大为 100G-1 字节。
CLOB 类型:用于指明变长的字母数字字符串,长度最大为 100G-1 字节。
BFILE 类型:用于指明存储在操作系统中的二进制文件。
举例:
CREATE TABLE users(
c1 TEXT,
c2 BLOB,
c3 CLOB,
c4 IMAGE
);
INSERT INTO users values('dameng','0x123456789','clob','0x987654321');
表类型
1、普通表
2、临时表
DM 临时表支持以下功能:
- 在临时表中,会话可以像普通永久表一样更新、插入和删除数据;
- 临时表的 DML 操作产生较少的 REDO 日志;
- 临时表支持建索引,以提高查询性能;
- 在一个会话或事务结束后,数据将自动从临时表中删除;
- 不同用户可以访问相同的临时表,每个用户只能看到自己的数据;
- 临时表的数据量很少,意味着更高效的查询效率;
- 临时表的表结构在数据删除后仍然存在,便于以后的使用;
- 临时表的权限管理跟普通表一致。
事务级和会话级
ON COMMIT DELETE ROWS:指定临时表是事务级的,每次事务提交或回滚之后,表中所有数据都被删除;
ON COMMIT PRESERVE ROWS:指定临时表是会话级的,会话结束时才清空表,并释放临时 B 树。
举例 创建一个事务级的临时表
Copy CREATE GLOBAL TEMPORARY TABLE TMP_EMP(
EMPNO INT PRIMARY KEY,
ENAME VARCHAR(15) NOT NULL,
JOB VARCHAR(10))
ON COMMIT DELETE ROWS;
3、分区表
概念:分区是指将表、索引等数据库对象划分为较小的可管理片段的技术,每一个片段称为分区子表或分区索引。一个表被分区后,对表的查询操作可以局限于某个分区进行,而不是整个表,这样可以大大提高查询速度。
在创建表的语法中,使用 partition 子句指定分区方式和分区列,以及分区的名字等信息,即可创建分区表。而分区子表可以指定 storage 子句,设置子表的存储属性,如所属表空间等;如果不指定,则继承分区主表的存储特性及表的其他属性。同时,支持多级分区表。
查看创建表sql:CALL SP_TABLEDEF('SYSDBA', 'users');
3.1范围分区表
CREATE TABLE users(
id int,
time DATETIME
)
PARTITION BY RANGE(time)(
PARTITION p1 VALUES LESS THAN ('2025-04-01'),
PARTITION p2 VALUES LESS THAN ('2025-07-01'),
PARTITION p3 VALUES LESS THAN ('2025-10-01'),
PARTITION p4 VALUES EQU OR LESS THAN ('2025-12-31') //'2025-12-31'也可替换为MAXVALUE
);
INSERT INTO users values(1,'2025-03-01'),(2,'2025-05-01');
SELECT * FROM users PARTITION (p1); 查询分区p1
3.2LIST分区表
对于字符型数据,取值比较固定的,则适合于采用 LIST 分区的方法。
CREATE TABLE users(
id INT,
city CHAR(20)
)
PARTITION BY LIST(city)(
PARTITION p1 VALUES ('北京', '天津'),
PARTITION p2 VALUES ('广州', '深圳')
);
INSERT INTO users values(1,'北京'),(2,'广州');
SELECT * FROM users PARTITION (p1);
3.3哈希分区表
CREATE TABLE users(
id INT,
city CHAR(20)
)
PARTITION BY HASH(city)(
PARTITION p1,
PARTITION p2
);
INSERT INTO users values(1,'北京'),(2,'广州');
SELECT * FROM users PARTITION (p1);
注意:哈希分区表分区名是匿名的,DM 统一使用 DMHASHPART+ 分区号(从 0 开始)作为分区名。
3.4创建多级分区表
CREATE TABLE users(
SALES_ID INT,
SALEDATE DATETIME,
CITY CHAR(10)
)
PARTITION BY LIST(CITY)
SUBPARTITION BY RANGE(SALEDATE) SUBPARTITION TEMPLATE(
SUBPARTITION P11 VALUES LESS THAN ('2012-04-01'),
SUBPARTITION P12 VALUES LESS THAN ('2012-07-01'),
SUBPARTITION P13 VALUES LESS THAN ('2012-10-01'),
SUBPARTITION P14 VALUES EQU OR LESS THAN (MAXVALUE))
(
PARTITION P1 VALUES ('北京', '天津')
(
SUBPARTITION P11_1 VALUES LESS THAN ('2012-10-01'),
SUBPARTITION P11_2 VALUES EQU OR LESS THAN (MAXVALUE)
),
PARTITION P2 VALUES ('上海', '南京', '杭州'),
PARTITION P3 VALUES (DEFAULT)
);
INSERT INTO users values(1,'2012-04-01','北京'),(2,'2012-04-01','上海'),(3,'2012-04-01','武汉');
SELECT * FROM users PARTITION (P1);
SELECT * FROM users SUBPARTITION (P11_2);
SELECT * FROM users SUBPARTITION (P3_P12);
4.列存储表(HUGE)
列存储:表的数据存储方式分为行存储和列存储。行存储是以记录为单位进行存储的,数据页面中存储的是完整的若干条记录;列存储是以列为单位进行存储的,每一个列的所有行数据都存储在一起,而且一个指定的页面中存储的都是某一个列的连续数据。
HUGE 表的存储方式有以下几个优点:
- 同一个列的数据都是连续存储的,可以加快某一个列的数据查询速度;
- 连续存储的列数据,具有更大的压缩单元和数据相似性,可以获得远优于行存储的压缩效率,压缩的单位是区;
- 条件扫描借助数据区的统计信息进行精确过滤,可以进一步减少 IO,提高扫描效率;
- 允许建立二级索引;
- 支持以 ALTER TABLE 的方式添加或者删除 PK 和 UNIQUE 约束。
4.1非事务型HUGE表
对非事务型 HUGE 表的增、删、改是直接对 HUGE 表进行写操作的,不写 UNDO 日志,不通过 BUFFER 缓存,直接操纵文件,速度快,但也因此导致不支持事务。另外,非事务型 HUGE 表中的 ROWID 是不固定的。
AUX辅助表:对于每个 HUGE 表,相应地配备一个 AUX 辅助表来管理其数据,AUX 辅助表是在创建 HUGE 表时系统自动创建的,表名为“表名$AUX”,如果该 HUGE 表为分区表,则辅助表名为“子表名$AUX”。
- COLID:表示当前这条记录对应的区所在的列的列 ID 号;
- SEC_ID:表示当前这个记录对应的区的区 ID 号,每一个区都有一个 ID 号,并且唯一;
- FILE_ID:表示这个区的数据所在的文件号;
- OFFSET:表示这个区的数据在文件中的偏移位置,4K 对齐;
- COUNT:表示这个区中存储的数据总数(有可能包括被删除的数据);
- ACOUNT:表示这个区中存储的实际数据行数;
- N_LEN:表示这个区中存储的数据在文件中的长度,4K 对齐的;
- N_NULL:表示这个区中的数据中包括的 NULL 值的行数;
- N_DIST:保留字段,无意义;
- CPR_FLAG:表示这个区是否压缩;
- ENC_FLAG:表示这个区是否加密;
- CHKSUM:用来存储标记位;
- MAX_VAL:表示这个区中的最大值,精确值;
- MIN_VAL:表示这个区中的最小值,精确值;
- SUM_VAL:表示这个区中所有值的和,精确值。
CREATE HUGE TABLE T1(A INT, B INT) STORAGE(WITHOUT DELTA);
4.2事务型HUGE表
通过增加 RAUX、DAUX 和 UAUX 行辅助表,减少了事务型 HUGE 表增、删、改操作的 IO,提高效率,同时提高并行性能。事务型 HUGE 表支持 UNDO 日志,实现了事务特性。
1、RAUX 行辅助表(插入):RAUX 行辅助表中内容对应于 HUGE 表中的最后一部分记录(不够存满一个数据区的)。当 RAUX 表中数据达到区大小时,将 RAUX 表中数据以列为单位写入各列对应的文件中,并清空 RAUX 表。RAUX 表与 HUGE 表结构相同,不论数据在哪个表中,每一行数据的 ROWID 固定不变。
2、DAUX 行辅助表(删除):DAUX 行辅助表中的一行数据对应 HUGE 表中一条或一段连续的被删除的数据,记录了被删除数据的起始行号和被删除数据的行数。删除 HUGE 表数据时,向 DAUX 行辅助表中插入对应删除数据的记录。
3、UAUX 行辅助表(更新):UAUX 行辅助表中一条记录对应 HUGE 表中更新列的一个值,记录了更新的列号、行号和更新后的值的二进制格式。更新 HUGE 表数据时,操作转换为对 UAUX 表的插入操作或更新操作。
CREATE HUGE TABLE T2 (A INT, B INT) STORAGE(WITH DELTA);
5、堆表
普通表都是以 B 树形式存放的,ROWID 都是逻辑的 ROWID,即从 1 一直增长下去。在并发情况下,每次插入过程中都需要逻辑生成 ROWID,这样影响了插入数据的效率;对于每一条数据都需要存储 ROWID 值,也会花费较大的存储空间。堆表是指采用了物理 ROWID 形式的表,即使用文件号、页号和页内偏移而得到 ROWID 值,这样就不需要存储 ROWID 值,可以节省空间。逻辑 ROWID 在插入或修改过程中,为了确保 ROWID 的唯一性,需要依次累加而得到值,这样就影响了效率,而堆表只需根据自己的文件号、页号和页内偏移就可以得到 ROWID,提高了效率。
普通表都是以 B 树形式存储在物理磁盘上,而堆表则采用一种“扁平 B 树”方式存储。
创建表方式
1、dm.ini LIST_TABLE
1)如果 LIST_TABLE = 1,则在未显式指定表是否为堆表或非堆表时,默认情况下创建的表为堆表;
2)如果 LIST_TABLE = 0,则在未显式指定表是否为堆表或非堆表时,默认情况下创建的表为普通表形式
2、SQL 语句显式指定
1)NOBRANCH:如果指定为 NOBRANCH,则创建的表为堆表,并发分支个数为 0,非并发分支个数为 1;
2)BRANCH(N,M):如果为该形式,则创建的表为堆表,并发分支个数为 N,非并发个数为 M;
3)BRANCH N:指定创建的表为堆表,并发分支个数为 N,非并发分支个数为 1;
4)CLUSTERBTR:创建的表为非堆表,即普通 B 树表。
举例:创建 LIST_TABLE 表有并发分支 2 个,非并发分支 4 个
CREATE TABLE LIST_TABLE(C1 INT) STORAGE(BRANCH (2,4));
索引
1、聚集索引和非聚集索引
聚集索引(又称为一级索引、主索引):聚集索引就是按照聚集索引键构造一棵 B+ 树,表数据存储在 B+ 树叶子节点上,通过定位索引可直接在 B+ 树中找到数据。每一个表有且只有一个聚集索引。
非聚集索引(又称为二级索引、辅助索引):将二级索引列和聚集索引列共同存储在 B+ 树叶子节点上。如果查找非聚集索引键值或聚集索引键值可直接在 B+ 树中找到;如果查找索引键值以外的数据,则需要回到一级索引中进行查找。每一个表可以有多个非聚集索引。
2、功能索引
唯一索引:索引数据根据索引键唯一;
函数索引:包含函数/表达式的预先计算的值;
位图索引:对低基数的列创建位图索引;
位图连接索引:针对两个或者多个表连接的位图索引,主要用于数据仓库中;
全文索引:在表的文本列上而建的索引;
空间索引:在空间数据上创建的索引,专用于 DMGEO 包中;
数组索引:在一个只包含单个数组成员的对象列上创建的索引;
普通索引:除了唯一索引、函数索引、位图索引、位图连接索引、全文索引、空间索引、数组索引以外的索引,均为普通索引。
3、虚索引和实索引
虚索引:指不存储数据的索引。虚索引由系统自动并隐式地创建,目的是保证约束的正确性。
举例:
CREATE TABLE T1 (C1 INT ,C2 INT UNIQUE);
CREATE TABLE T2(C1 INT,C2 INT REFERENCES T1(C2));
//查看约束对应的索引ID(INDEXID),假设为33555600
SELECT INDEXID FROM SYSCONS WHERE TABLEID=(SELECT ID FROM SYSOBJECTS WHERE NAME = 'T2');
行号 INDEXID
---------- -----------
1 33555600
//查看该索引是否为虚索引。3表示1+2。其中2代表虚索引。
SELECT FLAG FROM SYSINDEXES WHERE ID= 33555600;
行号 FLAG
---------- -----------
1 3
实索引:指存储数据的索引。虚索引以外的索引均为实索引。
4、单列索引和复合索引
单列索引:只有一个索引键的索引。
复合索引:含有多个索引键的索引。(联合索引)
5、全局索引和局部索引
全局索引:全局索引是以整张表的数据为对象而建立的索引。
全局非分区索引
//创建表
drop table t1;
create table t1(c1 int, c2 int, c3 int) partition by range(c1)
(
partition p1 values less than(100),
partition p2 values less than(200),
partition p3 values less than(maxvalue)
);
//创建全局非分区索引
create index idx1 on t1(c2) global;
//在WHERE中指定索引列,可使用索引idx1查询
explain select * from t1 where c2 = 130;
1 #NSET2: [1, 1, 24]
2 #PRJT2: [1, 1, 24]; exp_num(4), is_atom(FALSE)
3 #PARALLEL: [1, 1, 24]; scan_type(FULL), key_num(0, 0, 0), simple(0),range_sfun_opt(0)
4 #BLKUP2: [1, 1, 24]; IDX1(T1)
5 #SSEK2: [1, 1, 24]; scan_type(ASC), IDX1(T1), scan_range[130,130],is_global(1)
//直接对分区子表查询,则使用全局本地索引
explain select * from t1_p1 where c2 = 130;
1 #NSET2: [1, 1, 24]
2 #PRJT2: [1, 1, 24]; exp_num(4), is_atom(FALSE)
3 #BLKUP2: [1, 1, 24]; INDEX3505_3504(T1_P1)
4 #SSEK2: [1, 1, 24]; scan_type(ASC), INDEX3505_3504(T1_P1), scan_range[130,130],is_global(1)
全局分区索引(DMDPC)
//创建表空间
create tablespace ts1 datafile 'opt/ts/ts01.dbf' size 128 storage (on raft_1);
create tablespace ts2 datafile 'opt/ts/ts02.dbf' size 128 storage (on raft_2);
//创建表
drop table t1;
create table t1(c1 int, c2 int, c3 int) partition by range(c1)
(
partition p1 values less than(100),
partition p2 values less than(200),
partition p3 values less than(maxvalue)
);
//创建全局分区索引
drop index idx1;
create index idx1 on t1(c2) global partition by range(c2)
(
partition p1 values less than(1000) storage(on ts2) ,
partition p2 values less than(maxvalue) storage(on ts1)
);
//使用索引idx1查询
explain select * from t1 where c2 = 130; //当数据量很大时
explain select * from t1 index idx1 where c2 = 130; //或直接指定全局索引
1 #NSET2: [2, 1, 24]
2 #ERECV: [2, 1, 24]; stask_no(-1), l_stask_no(1), n_key(0), in_turn(0), trig(0)
3 #ESEND: [2, 1, 24]; stask_no(1), type(DIRECT), sites(2:1,1:1), sql_invoke(0), pwj_opt(0), table(-); INFO_BITS(0x8)
4 #PRJT2: [2, 1, 24]; exp_num(4), is_atom(FALSE)
5 #PARALLEL: [2, 1, 24]; scan_type(FULL), key_num(0, 0, 0), simple(0), range_sfun_opt(0)
6 #BLKUP2: [2, 1, 24]; IDX1(T1)
7 #ERECV: [2, 1, 24]; stask_no(1), l_stask_no(0), n_key(0), in_turn(0), trig(0)
8 #ESEND: [2, 1, 24]; stask_no(0), type(ROWID), sites(2:1,1:1), sql_invoke(0), pwj_opt(0), table(T1) empty_type(ERROR); INFO_BITS(0xc)
9 #GI: [2, 1, 24]; policy(RANDOM), gi_unit[0..0], scan_type[0](FULL)
10 #SSEK2: [2, 1, 24]; scan_type(ASC), IDX1(T1), scan_range[130,130],is_global(1)
//直接对分区子表查询,则使用全局本地索引
explain select * from t1_p1 where c2 = 130;
1 #NSET2: [1, 1, 24]
2 #PRJT2: [1, 1, 24]; exp_num(4), is_atom(FALSE)
3 #BLKUP2: [1, 1, 24]; INDEX3505_3504(T1_P1)
4 #SSEK2: [1, 1, 24]; scan_type(ASC), INDEX3505_3504(T1_P1), scan_range[130,130],is_global(1)引
局部索引
//创建表
drop table t1;
create table t1(c1 int, c2 int, c3 int) partition by range(c1)
(
partition p1 values less than(100),
partition p2 values less than(200),
partition p3 values less than(maxvalue)
);
//创建局部索引,必须缺省GLOBAL关键字
create index idx1 on t1(c2);
//在WHERE中指定索引列,可使用索引idx1查询
explain select * from t1 where c2 = 130;
1 #NSET2: [1, 1, 24]
2 #PRJT2: [1, 1, 24]; exp_num(4), is_atom(FALSE)
3 #PARALLEL: [1, 1, 24]; scan_type(FULL), key_num(0, 0, 0), simple(0), range_sfun_opt(0)
4 #BLKUP2: [1, 1, 24]; IDX1(T1)
5 #SSEK2: [1, 1, 24]; scan_type(ASC), IDX1(T1), scan_range[130,130],is_global(0)
//直接对分区子表查询,则使用子表局部索引
explain select * from t1_p1 where c2 = 130;
1 #NSET2: [1, 1, 24]
2 #PRJT2: [1, 1, 24]; exp_num(4), is_atom(FALSE)
3 #BLKUP2: [1, 1, 24]; INDEX3505_3504(T1_P1)
4 #SSEK2: [1, 1, 24]; scan_type(ASC), INDEX3505_3504(T1_P1), scan_range[130,130],is_global(0)
2245

被折叠的 条评论
为什么被折叠?



