DM数据库名词解释

1. 模式及模式对象

1.1 模式

用户的模式(SCHEMA)指的是用户账号拥有的对象集,在概念上可将其看作是包含表、视图、索引和权限定义等的对象。如下图:

 

如上面三图,DM模式主要包含以下的模式对象:

1.表;2.索引;3.视图;

4.存储过程/函数;5.序列;6.触发器;

7.包;8.类;9.同义词;10.域;

11.全文索引;12.外部链接。

采用模式的原因有几点:

1.允许多个用户使用一个数据库而不会干扰其它用户;

2.把数据库对象组织成逻辑组,让它们更便于管理;

3.第三方的应用可以放在不同的模式中,这样可以避免和其它对象的名字冲突。

模式类似于操作系统层次的目录,只不过模式不能嵌套。

也就是说,模式下的对象可以看成目录下的文件;角色可以看成操作系统的用户组;操作中权限分为读、写、执行,而数据库中的权限分类比较细,对于不同的数据库对象,有不同的权限。

(执行类型,执行类,执行目录是什么意思)

Linux中,对目录的执行权限指的是可以进入目录。不知道这里的数据库对象中目录是什么意思。

模式对象之外的其他对象统一称为非模式对象,非模式对象主要包括以下几种对象:

1.角色;2.用户;3.表空间;4.权限;

5.其他(目录,上下文应该是,其他的不知道算不算数据库对象),如下图:

1.2 表

表是数据库中数据存储的基本单元,是对用户数据进行读和操纵的逻辑实体。表由列和行组成,每一行代表一个单独的记录。表中包含一组固定的列,表中的列描述该表所跟踪的实体的属性,每个列都有一个名字及各自的特性(数据类型和长度)。

1.2.1 完整性

数据库系统概论:

关系模型的完整性规则是对关系的某种约束条件。

关系模型中有三类完整性约束:实体完整性、参照完整性和用户定义的完整性。其中实体完整性和参照完整性是关系模型必须满足的。

实体完整性:组成主键的列都不能为空,主键不重复

参照完整性:也称为引用完整性,参照关系中的属性值必须能够在被参照关系找到或者取空值。

用户定义完整性:

用户自定义完整性是针对某一具体关系数据库的约束条件,它反映某一具体应用所涉及的数据必须满足的语义要求。主要包括非空约束、唯一约束、检查约束、缺省约束、主键约束、外键约束。

Not null、unique、check、default、primary key、foreign key

DM8系统管理员手册:

实体完整性定义表中的所有行能唯一地标识,一般用主键、唯一索引、UNIQUE 关键字及 IDENTITY 属性来定义;

域完整性通常指数据的有效性,限制数据类型、缺省值、规则、约束、是否可以为空等条件,域完整性可以确保不会输入无效的值;

参考完整性维护表间数据的有效性、完整性,通常通过建立外键联系另一表的主键来实现。

DM数据库的表可以分为两类,分别为数据库内部表和外部表,数据库内部表由数据库管理系统自行组织管理,而外部表在数据库的外部组织,是操作系统文件。其中内部表包括:数据库基表、HUGE表和水平分区表。

本节里的小节是按照DM8系统管理员使用手册和管理工具上显示的内容来区分的。

1.2.2 普通表

CREATE TABLE <表名定义> <表结构定义>;

行迁移选项仅对水平分区表有效,其他表类型自动忽略。

1.2.3 临时表

CREATE [GLOBAL] TEMPORARY TABLE <表名定义> <表结构定义>;

GLOBAL:目前仅支持 GLOBA临时表,因此建临时表时是否指定 GLOBA效果是一样的;

默认存储在TEMP表空间中,不允许使用其他表空间

会话级临时表是指临时表中的数据只在会话生命周期之中存在,当用户退出会话结束的时候,Oracle自动清除临时表中数据。通过语句:ON COMMIT PRESERVE ROWS指定。

事务级临时表是指临时表中的数据只在事务生命周期中存在。当一个事务结束(commit or rollback),Oracle自动清除临时表中数据。通过语句:ON COMMIT DELETE ROWS指定。

缺省情况下是事务级。

1.2.4 HUGE表(列存储表)

列存储是以列为单位进行存储的,每一个列的所有行数据都存储在一起,而且一个指定的页面中存储的都是某一个列的连续数据。

Huge File System(检查 HFS)是达梦数据库实现的,针对海量数据进行分析的一种高效、简单的列存储机制。列存储表(也称为 HUGE 表)就是建立在 HFS 存储机制上的一种表。

HUGE 表是建立在自己特有的表空间 HTS(HUGE TABLESPACE,即 HUGE 表空间)上的。最多可创建 32767 个 HUGE 表空间,其相关信息存储在动态视图 V$HUGE_TABLESPACE中。

这个表空间与普通的表空间不同。普通的表空间,数据是通过段、簇、页来管理的,并且以固定大小(4K、8K、16K、32K)的页面为管理单位;而 HUGE 表空间是通过 HFS 存储机制来管理的,它相当于一个文件系统。创建一个 HTS,其实就是创建一个空的文件目录。在创建一个 HUGE 表并插入数据时,数据库会在指定的 HTS 表空间目录下创建一系列的目录及文件。

当用户在创建HUGE表时,未指定HTS表空间的情况下,HMAIN表空间会充当默认HTS表空间。

CREATE HUGE TABLE <表名定义> <表结构定义>[<PARTITION 子句>] <STORAGE 子句 1> <日志属性>;

日志选项设置仅对非事务型 HUGE 表有效,支持通过做日志来保证数据的完整性。完整性保证策略主要是通过数据的镜像来实现的,镜像的不同程度可以实现不同程度的完整性恢复。三种选择:

1)LOG NONE:不做镜像。相当于不做数据一致性的保证,如果出错只能手动通过系统函数 SF_REPAIR_HFS_TABLE(模式名,表名)来修复表数据。2)LOG LAST:做部分镜像。但是在任何时候都只对当前操作的区做镜像,如果当前区的操作完成了,这个镜像也就失效了,并且可能会被下一个被操作区覆盖,这样做的好处是镜像文件不会太大,同时也可以保证数据是完整的。但有可能遇到的问题是:一次操作很多的情况下,有可能一部分数据已经完成,另一部分数据还没有来得及做的问题。

3)LOG ALL:全部做镜像。在操作过程中,所有被修改的区都会被记录下来,当一次操作修改的数据过多 时,镜像文件有可能会很大,但能够保证操作完整性。

默认选择为 LOG LAST;

<STORAGE 子句>中,WITH DELTA 表示创建事务型 HUGE 表;WITHOUT DELTA 表示创建非事务型 HUGE 表,缺省为 WITH DELTA。若创建数据库时使用参数HUGE_WITH_DELTA的缺省值1,则不支持创建非事务型 HUGE 表。

HUGE_WITH_DELTA这个参数应该只能建库时指定,可能无法修改。

select * from V$dm_ini where para_name like '%HUGE%';

图形化中,默认创建非事务型HUGE表,找不到如何创建事务型HUGE表,可以通过ddl语句创建。

对非事务型 HUGE 表的增、删、改是直接对 HUGE 表进行写操作的,不写 UNDO 日志,不通过 BUFFER 缓存,直接操纵文件,速度快,但也因此导致不支持事务。另外,非事务型 HUGE 表中的 ROWID 是不固定的。

对于每个 HUGE 表,相应地配备一个 AUX 辅助表来管理其数据。因为在 HUGE 表文件中只存储了数据,辅助表用来管理以及辅助系统用户操作这些数据,AUX 辅助表是在创建HUGE 表时系统自动创建的,表名为―表名$AUX,如果该 HUGE 表为分区表,则辅助表名为“子表名$AUX”。辅助表的表名长度不能大于 128 个字节。AUX 辅助表中每一条记录对应文件中的一个数据区。包含以下15列:

1.COLID:表示当前这条记录对应的区所在的列的列 ID 号;2.SEC_ID:表示当前这个记录对应的区的区 ID 号,每一个区都有一个 ID 号,并且唯一;3.FILE_ID:表示这个区的数据所在的文件号;4.OFFSET:表示这个区的数据在文件中的偏移位置,4K 对齐;5.COUNT:表示这个区中存储的数据总数(有可能包括被删除的数据);6.ACOUNT:表示这个区中存储的实际数据行数;7.N_LEN:表示这个区中存储的数据在文件中的长度,4K 对齐的;

8.N_NULL:表示这个区中的数据中包括的 NUL值的行数;9.N_DIST:表示这个区中所有数据互不相同的行数;10.CPR_FLAG:表示这个区是否压缩;11.ENC_FLAG:表示这个区是否加密;12.CHKSUM:用来存储标记位;13.MAX_VAL:表示这个区中的最大值,精确值;14.MIN_VAL:表示这个区中的最小值,精确值;15.SUM_VAL:表示这个区中所有值的和,精确值。前面 7 列是用来控制数据存取的,根据这些信息就可以知道这个区的具体存储位置、长度及基本信息。后面 8 列都是用来对这个区进行统计分析的。其中,COLID 和 SEC_ID的组合键为辅助表的聚集关键字。

非事务型 HUGE 表在进行增、删、改时直接对 HUGE 表进行写操作,每次写操作需要至少对一个区进行 IO,导致 IO 量较大,且并发性能不高。

为此,DM 推出了事务型 HUGE 表,通过增加 RAUX、DAUX 和 UAUX 行辅助表,减少了事务型 HUGE 表增、删、改操作的 IO,提高效率,同时提高并行性能。事务型 HUGE 表支持 UNDO 日志,实现了事务特性。

RAUX 行辅助表存放最后一个数据区(不够存满一个数据区)的数据,表名为―HUGE 表名$RAUX。

DAUX 行辅助表记录 HUGE 表数据文件中被删除的数据,表名为―HUGE 表名$DAUX。

UAUX 行辅助表记录 HUGE 表被更新的数据的新值,表名为―HUGE 表名$UAUX。

HUGE表疑似无法使用图形浏览和插入数据。

select top 0, 100 ROWID, * from "TEST"."TEST_HT1"

HUGE 表与普通行表一样,可以进行增、删、改操作,操作方式也是一样的。但 HUGE表的删除与更新操作的效率会比行表低一些,并发操作性能也会比行表差一些,因此在HUGE中不宜做频繁的删除及更新操作。总之,HUGE 表比较适合做分析型表的存储。

1.2.5 分区表

分区是指将表、索引等数据库对象划分为较小的可管理片段的技术,每一个片段称为分区子表或分区索引。一个表被分区后,对表的查询操作可以局限于某个分区进行,而不是整个表,这样可以大大提高查询速度。

DM采用子表方式创建分区表,分区表作为分区主表,而每一个分区以一个子表实体存在,即每一个分区都是一个完整的表,一般命名为主表名_分区名。对于水平分区,子表跟主表具有相同的逻辑结构,即分区子表与分区主表有相同的列定义和约束定义。在 DM 分区表中,主表本身不存储数据,所有数据只存储在子表中,从而实现不同分区的完全独立性。水平分区子表删除后,会将子表上的数据一起删除。

不同分区可以存储于相同表空间,也可以位于不同的表空间中。

和MPP集群中分布表的概念类似,分布表是把数据存储在不同节点上,分区表就是把数据存储在不同分区上。

水平分区:

达梦数据库 DM 支持对表进行水平分区。对于水平分区,提供以下分区方式:1.范围(range)水平分区:对表中的某些列上值的范围进行分区,根据某个值的范围,决定将该数据存储在哪个分区上;

2.哈希(hash)水平分区:通过指定分区编号来均匀分布数据的一种分区类型,通过在 I/O 设备上进行散列分区,使得这些分区大小基本一致;3.列表(list)水平分区:通过指定表中的某个列的离散值集,来确定应当存储在一起的数据。例如,可以对表上的 status 列的值在('A','H','O')放在一个分区,值在('B','I','P')放在另一个分区,以此类推;4.多级分区表:按上述三种分区方法进行任意组合,将表进行多次分区,称为多级分区表。(这个应该得用sql语句才行,图形化找不到)

在创建表的语法中,使用 partition 子句指定分区方式和分区列,以及分区的名字等信息,即可创建分区表。

行迁移选项仅对水平分区表有效,其他表类型自动忽略:

ENABLE ROW MOVEMENT 打开行迁移,允许更新后数据发生跨分区的移动。DISABLE ROW MOVEMENT,关闭行迁移,不允许更新后数据发生跨分区的移动。缺省为DISABLE ROW MOVEMENT;

分区列类型必须是数值型、字符型或日期型,不支持 BLOB、CLOB、IMAGE、TEXT、LONGVARCHAR、BIT、BINARY、VARBINARY、LONGVARBINARY、BFILE、时间间隔类型和用户自定义类型为分区列;

--要想在表的开始范围或中间增加分区,应使用 SPLIT PARTITION 语句。

--分区管理不支持图形化

--查看表的所有分区信息

select table_name,partition_name from all_tab_partitions where table_owner='OETHIS_TV' and table_name='ET_AIRPORTCONTROL';

--创建哈希分区表:

create table TAB_15(c1 int , c2 char(10))

partition by hash(c1)(

partition p1 ,

partition p2 ,

partition p3

);

insert into TAB_15 values(1, 'a');

insert into TAB_15 values( 2, 'b');

insert into TAB_15 values( 3, 'c');

insert into TAB_15 values( 4, 'd');

insert into TAB_15 values( 5, 'e');

insert into TAB_15 values( 6, 'f');

--查询分区表某分区里的数据

SELECT * FROM TAB_15 PARTITION (p3);

select * from TAB_15_p3;

select * from sysobjects where name like '%TAB_15%';

create table TAB_15_p3(C1 int);

SELECT * FROM SYS.SYSHPARTTABLEINFO;

多级分区表的查询:

--建水平分区表的组合分区表:

CREATE TABLESPACE T1 DATAFILE 'T1.DATA' SIZE 32;

CREATE TABLESPACE T2 DATAFILE 'T2.DATA' SIZE 32;

CREATE TABLESPACE T3 DATAFILE 'T3.DATA' SIZE 32;

create table "SYSDBA"."T_SUN"

(

"C1" CHAR(10),

"C2" INT,

"C3" CHAR(10)

)

partition by list("C1")

subpartition by range("C2")

subpartition template

(

subpartition "PART_1" values less than(10) storage(on "T1"),

subpartition "PART_2" values less than(20) storage(on "T2"),

subpartition "PART_3" values less than(MAXVALUE) storage(on "T3")

)

(

partition "PART_1" values('A') storage(on "T1"),

partition "PART_2" values('B') storage(on "T2"),

partition "PART_3" values('C') storage(on "T3")

);

insert into T_SUN values('A',5,'');

insert into T_SUN values('A',12,'');

insert into T_SUN values('A',25,'');

insert into T_SUN values('B',5,'');

insert into T_SUN values('B',12,'');

insert into T_SUN values('C',12,'');

select * from T_SUN_PART_1;

SELECT * FROM T_SUN PARTITION (PART_1);

select * from T_SUN_PART_1_PART_2;

select * from T_SUN_PART_1 PARTITION (PART_2);

--以下三条都可以查询成功。

calsp_tabledef('SYSDBA','T_SUN');

calsp_tabledef('SYSDBA','T_SUN_PART_1');

calsp_tabledef('SYSDBA','T_SUN_PART_1_PART_2');

垂直分区:

不支持垂直分区表。

1.2.6 堆表

简单地说,堆表是指采用了物理 ROWID 形式的表,即使用文件号、页号和页内偏移而得到 ROWID 值,这样就不需要存储 ROWID 值,可以节省空间。

堆表的创建有两种方式,一种是采用在配置文件 dm.ini 中设置参数,一种是在建表语句中显式指定堆表选项。

1.INI 参数方式用户可以在配置文件中,添加 LIST_TABLE 参数:1) 如果 LIST_TABLE = 1,则在未显式指定表是否为堆表或非堆表时,默认情况下创建的表为堆表;2) 如果 LIST_TABLE = 0,则在未显式指定表是否为堆表或非堆表时,默认情况下创建的表为普通表形式。2.SQ**语句显示指定**不管参数 LIST_TABLE 设置为何值,创建表时可以在 STORAGE 选项中指定需要创建的表形式, 与堆表创建形式相关的关键字有三个,分别是 NOBRANCH、BRANCH、CLUSTERBTR。<BRANCH数>取值范围为1~64,<NOBRANCH 数>取值范围为 1~64。1) NOBRANCH:如果指定为 NOBRANCH,则创建的表为堆表,并发分支个数为 0,非并发分支个数为 1;2) BRANCH(n,m):如果为该形式,则创建的表为堆表,并发分支个数为 n,非并发个数为 m;3) BRANCH n:指定创建的表为堆表,并发分支个数为 n,非并发分支个数为 0;4) CLUSTERBTR:创建的表为非堆表,即普通 B 树表。

堆表不支持列存储。

1.2.7 外部表

外部表的定义见DM8_SQL语言使用手册

1.2.8 查看表信息

CALSP_TABLEDEF('SYSDBA', 'EMPLOYEE');查看表的定义

SELECT IDENT_CURRENT('SYSDBA.IDENT_TABLE'); 自增列的当前值;

SELECT IDENT_SEED('SYSDBA.IDENT_TABLE'); 自增列的种子信息;

SELECT IDENT_INCR('SYSDBA.IDENT_TABLE');自增列的增量信息

SELECT TABLE_USED_SPACE('SYSDBA','SPACE_TABLE');已分配给表的页面数

SELECT TABLE_USED_PAGES('SYSDBA','SPACE_TABLE');表已使用的页面数

1.3 索引

DM8 中表(列存储表和堆表除外)都是使用 B+树索引结构管理的,每一个普通表都有且仅有一个聚集索引,数据都通过聚集索引键排序,根据聚集索引键可以快速查询任何记录。

1.3.1 索引原理探究

在关系数据库中,索引是一种单独的、物理的数对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。

索引提供指向存储在表的指定列中的数据值的指针,然后根据您指定的排序顺序对这些指针排序。数据库使用索引以找到特定值,然后顺指针找到包含该值的行。这样可以使对应于表的SQL语句执行得更快,可快速访问数据库表中的特定信息。

其实把索引理解为图书目录,就非常好理解了。

1.3.1.1 B树与B+树

相对于cpu和内存操作,磁盘IO开销很大,非常容易成为系统的性能瓶颈,因此计算机操作系统做了一些优化:

当一次IO时,将相邻的数据也都读取到内存缓冲区内,而不是仅仅读取当前磁盘地址的数据。因为局部预读性原理告诉我们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。每一次IO读取的数据我们称之为一页(page)。具体一页有多大数据跟操作系统有关,一般为4k或8k,也就是我们读取一页内的数据时候,实际上才发生了一次IO,这个理论对于索引的数据结构设计非常有帮助。

为什么索引能提升数据库查询效率呢?根本原因就在于索引减少了查询过程中的IO次数。那么它是如何做到的呢?使用B+树。下面先简单了解一下B树和B+树。

B树,即平衡多路查找树(B-Tree),是为磁盘等外存储设备设计的一种平衡查找树。

m叉查找树中,规定除了根节点外,任何结点至少有⌈m/2⌉个分叉,即⾄少含有⌈m/2⌉−1个关键字

再看B+树相对于B树的两个特点:

数据只出现在叶子节点

所有叶子节点增加了一个链指针

在B+树中,非叶结点不含有该关键字对应记录的存储地址。可以使⼀个磁盘块可以包含更多个关键字,使得B+树的阶更⼤,树高更矮,读磁盘次数更少,查找更快。

但是,为什么是B+树而不是B树呢?原因有两点:

B树每个节点中不仅包含数据的key值,还有data值。而每一个页的存储空间是有限的,如果data数据较大时将会导致每个节点能存储的key的数量很小,要保存同样多的key,就需要增加树的高度。树的高度每增加一层,查询时的磁盘I/O次数就增加一次,进而影响查询效率。而在B+Tree中,所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储key值信息,这样可以大大加大每个节点存储的key值数量,降低B+树的高度。

B+树的叶子节点上有指针进行相连,因此在做数据遍历的时候,只需要对叶子节点进行遍历即可,这个特性使得B+树非常适合做范围查询。

1.3.1.2 聚簇索引与非聚簇索引

首先,为了方便理解,我们先了解一下聚集索引(clustered index)和非聚集索引(secondary index,也称辅助索引或普通索引)。这两种索引是按存储方式进行区分的。

聚集索引(clustered)也称聚簇索引,这种索引中,数据库表行中数据的物理顺序与键值的逻辑(索引)顺序相同。一个表的物理顺序只有一种情况,因此对应的聚集索引只能有一个。

如果某索引不是聚集索引,则表中的行物理顺序与索引顺序不匹配,与非聚集索引相比,聚集索引有着更快的检索速度。

如果不好理解,请看下面这个表:

idnamescore物理地址
1叶良辰780×01
2龙傲天880×02
3赵日天560×03
4徐胜虎770×04

表中id和物理地址是保持一致顺序的,id较大的行,其物理地址也比较靠后。因为聚集索引的特性,它的建立有一定的特殊要求:

在Innodb中,聚簇索引默认就是主键索引。

如果表中没有定义主键,那么该表的第一个唯一非空索引被作为聚集索引。

如果没有主键也没有合适的唯一索引,那么innodb内部会生成一个隐藏的主键作为聚集索引,这个隐藏的主键是一个6个字节的列,改列的值会随着数据的插入自增。

大家还记得,自增主键和uuid作为主键的区别么?由于主键使用了聚集索引,如果主键是自增id,那么对应的数据一定也是相邻地存放在磁盘上的,写入性能比较高。如果是uuid的形式,频繁的插入会使innodb频繁地移动磁盘块,写入性能就比较低了。

1.3.2 索引原理图示

下面用一个通过主键索引查找数据的案例演示一下索引的原理。假如有student表如下,id上建立了聚集索引,name上建立非聚集索引:

idnamescore
2叶良辰78
4龙傲天88
10赵日天56
11徐胜虎77

1.3.2.1 聚簇索引

MySQL中最常见的两种存储引擎分别是MyISAM和InnoDB,分别实现了「非聚集索引(普通索引)「和」聚集索引」;

聚集索引:「聚集索引的顺序就决定了数据行的物理存储顺序」;所以我们创建的主键索引其实就是聚集索引,如果未定义主键,MYSQL会默认选择非空的唯一索引当作主键,否则会默认生成一个主键

非聚集索引:「索引顺序与数据行物理排列顺序无关」;

当我们执行下面的语句时,

SELECT name FROM student WHERE id=2

查询过程如下图所示:

用语言描述一下,是这样的:

先找到根节点所在磁盘块,读入内存。(第1次磁盘I/O操作)

在内存中判断id=3所在区间(0,8),找到该区间对应的指针1(第1次内存查找)

根据指针1记录的磁盘地址,找到磁盘块2并读入内存(第2次磁盘I/O操作)

在内存中判断id=3所在区间(0,4),找到该区间对应的指针2(第2次内存查找)

根据指针2记录的磁盘地址,找到磁盘块4并读入内存(第3次磁盘I/O操作)

在内存中查找到id=2对应的数据行记录(第3次内存查找)

我们知道,磁盘I/O相对于内存运算(尤其内存中的主键是有序排列的,利用二分查找等算法效率非常高)耗时高得多,因此在数据库查询中,减少磁盘访问时数据库的性能优化的主要手段。

而分析上面过程,发现整个查询只需要3次磁盘I/O操作(其实InnoDB引擎是将根节点常驻内存的,第1次磁盘I/O操作并不存在)和3次内存查找操作。相对于不使用索引的遍历式查找,大大减少了对磁盘的访问,因此查找效率大幅提高。但是,因为索引树要与表中数据保持一致,因此当表发生数据增删改时,索引树也要相应修改,导致写数据比没有索引时开销大一些。

1.3.2.2 非聚簇索引

如上图,多加一个索引,就会多生成一颗非聚簇索引树。因此,索引不能随意增加。在做写库操作的时候,需要同时维护这几颗树的变化,导致效率降低!

另外,仔细观察的人一定会发现,不同于聚集索引,非聚集索引叶子节点上不再是真实数据,而是存储了索引字段自身值和主键索引。因此,当我们执行以下SQL语句时:

SELECT id,name FROM student WHERE name='叶良辰';

整个查询过程与聚集索引的过程一样,只需要扫描一次索引树(n次磁盘I/O和内存查询),即可拿到想要的数据。

但是,如果查询name索引树没有的数据时,情况就不一样了:

SELECT score FROM student WHERE name='叶良辰';

注意看上图中的红色箭头,因为扫描完name索引后,Mysql只能获取到对应的id和name,然后用id的值再去聚集索引中去查询score的值。这个过程相对于聚集索引查询的效率下降,可以理解了吧。

这就是通常所说的回表或者二次查询:使用聚集索引查询可以直接定位到记录,而普通索引通常需要扫描两遍索引树,即先通过普通索引定位到主键值,在通过聚集索引定位到行记录,这就是所谓的回表查询,它的性能比扫描一遍索引树低。

1.3.2.3 联合索引

既然普通索引会导致回表二次查询,那么有什么办法可以应对呢?建立联合索引!

所谓联合索引,也称多列索引,就是建立在多个字段上的索引,这个概念是跟单列索引相对的。联合索引依然是B+树,但联合索引的健值数量不是一个,而是多个。构建一颗B+树只能根据一个值来构建,因此数据库依据联合索引最左的字段来构建B+树。

例如在a和b字段上建立联合索引,索引结构将如下图所示:

一目了然,当我们再执行SELECT score FROM student WHERE name='叶良辰';时,可以直接通过扫描非聚集索引直接获取score的值,而不再需要到聚集索引上二次扫描了。

1.3.2.4 最左前缀匹配

联合索引中有一个重要的课题,就是最左前缀匹配。

最左前缀匹配原则:在MySQL建立联合索引时会遵守最左前缀匹配原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。

这是为什么呢?我们再仔细观察索引结构,可以看到索引key在排序上,首先按a排序,a相等的节点中,再按b排序。因此,如果查询条件是a或a和b联查时,是可以应用到索引的。如果查询条件是单独使用b,因为无法确定a的值,因此无法使用索引。

假如在table表的a,b,c三个列上建立联合索引,简要分类分析下联合索引的最左前缀匹配。

首先看等值查询:

1、全值匹配查询时(where子句搜索条件顺序调换不影响索引使用,因为查询优化器会自动优化查询顺序 ),可以用到联合索引

SELECT * FROM table WHERE a=1 AND b=3 AND c=2

SELECT * FROM table WHERE b=3 AND c=4 AND a=2

2、匹配左边的列时,可以用到联合索引

SELECT * FROM table WHERE a=1

SELECT * FROM table WHERE a=1 AND b=3

3、未从最左列开始时,无法用到联合索引

SELECT * FROM table WHERE b=1 AND b=3

4、查询列不连续时,无法使用联合索引(会用到a列索引,但c排序依赖于b,所以会先通过a列的索引筛选出a=1的记录,再在这些记录中遍历筛选c=3的值,是一种不完全使用索引的情况)

SELECT * FROM table WHERE a=1 AND c=3

再看范围查询:

1、范围查询最左列,可以使用联合索引

SELECT * FROM table WHERE a>1 AND a<5;

2、精确匹配最左列并范围匹配其右一列(a值确定时,b是有序的,因此可以使用联合索引)

SELECT * FROM table WHERE a=1 AND b>3;

3、精确匹配最左列并范围匹配非右一列(a值确定时,c排序依赖b,因此无法使用联合索引,但会使用a列索引筛选出a>2的记录行,再在这些行中条件 c >3逐条过滤)

SELECT * FROM table WHERE a>2 AND c>5;

1.3.3 索引的正确使用姿势

索引的优点如下:

通过创建唯一索引可以保证数据库表中每一行数据的唯一性。

可以大大加快数据的查询速度,这是使用索引最主要的原因。

在实现数据的参考完整性方面可以加速表与表之间的连接。

在使用分组和排序子句进行数据查询时也可以显著减少查询中分组和排序的时间。

既然索引这么好,那么我们是不是尽情使用索引呢?非也,索引优点明显,但相对应,也有缺点:

创建和维护索引组要耗费时间,并且随着数据量的增加所耗费的时间也会增加。

索引需要占磁盘空间,除了数据表占数据空间以外,每一个索引还要占一定的物理空间。

当对表中的数据进行增加、删除和修改的时候,索引也要动态维护,这样就降低了数据的维护速度。

因此,使用索引时要兼顾索引的优缺点,寻找一个最有利的平衡点。

1):表的主键和包含唯一约束的列自动创建索引,所以在建立唯一约束时,可以考虑该列是否必要建立,是否要作为查询条件。

2):如果某个表的数据量较大(十几二十万以上),某列经常作为where的查询条件,并且检索的出来的行数经常是小于总表的5%,那该列可以考虑建立索引。

3)对于两表连接的字段,应该考虑建立索引。如果经常在某表的一个字段进行Order By 则也考虑建立索引

2.1 索引的类型区分

以InnoDB引擎为例,Mysql索引可以做如下区分。

首先,索引可以分为聚集索引和非聚集索引,它们的区别和含义在前文有大幅介绍,此处不再赘述。

其次,从逻辑上,索引可以区分为:

普通索引:普通索引是 MySQ中最基本的索引类型,它没有任何限制,唯一任务就是加快系统对数据的访问速度。普通索引允许在定义索引的列中插入重复值和空值。

唯一索引:唯一索引与普通索引类似,不同的是创建唯一性索引的目的不是为了提高访问速度,而是为了避免数据出现重复。唯一索引列的值必须唯一,允许有空值。如果是组合索引,则列值的组合必须唯一。创建唯一索引通常使用 UNIQUE 关键字。例如在student 表中的 id 字段上建立名为 index_id 的索引CREATE UNIQUE INDEX index_id ON tb_student(id);

主键索引:主键索引就是专门为主键字段创建的索引,也属于索引的一种。主键索引是一种特殊的唯一索引,不允许值重复或者值为空。创建主键索引通常使用 PRIMARY KEY 关键字。不能使用 CREATE INDEX 语句创建主键索引。

空间索引:空间索引是对空间数据类型的字段建立的索引,空间索引主要用于地理空间数据类型 ,很少用到。

全文索引:全文索引主要用来查找文本中的关键字,只能在 CHAR、VARCHAR 或 TEXT 类型的列上创建。在 MySQ中只有 MyISAM 存储引擎支持全文索引。全文索引允许在索引列中插入重复值和空值。

索引在实际使用上分为单列索引和多列索引。

单列索引:单列索引就是索引只包含原表的一个列。在表中的单个字段上创建索引,单列索引只根据该字段进行索引。

例如在student 表中的 address 字段上建立名为 index_addr 的单列索引,address 字段的数据类型为 VARCHAR(20),索引的数据类型为 CHAR(4)。SQ语句如下:

CREATE INDEX index_addr ON student(address(4));

这样,查询时可以只查询 address 字段的前 4 个字符,而不需要全部查询。

多列索引也称为复合索引或组合索引。相对于单列索引来说,组合索引是将原表的多个列共同组成一个索引。

多列索引是在表的多个字段上创建一个索引。该索引指向创建时对应的多个字段,可以通过这几个字段进行查询。但是,只有查询条件中使用了这些字段中第一个字段时,索引才会被使用。

下面在 student 表中的 name 和 address 字段上建立名为 index_na 的索引,SQ语句如下:

CREATE INDEX index_na ON tb_student(name,address);

该索引创建好了以后,查询条件中必须有 name 字段才能使用索引。

一个表可以有多个单列索引,但这些索引不是组合索引。一个组合索引实质上为表的查询提供了多个索引,以此来加快查询速度。比如,在一个表中创建了一个组合索引(c1,c2,c3),在实际查询中,系统用来实际加速的索引有三个:单个索引(c1)、双列索引(c1,c2)和多列索引(c1,c2,c3)。

1.3.4 索引失效场景

创建了索引并不意味着高枕无忧,在很多场景下,索引会失效。下面列举了一些导致索引失效的情形,是我们写SQL语句时应尽量避免的。

1、条件字段原因

单字段有索引,WHERE条件使用多字段(含带索引的字段),例如 SELECT * FROM student WHERE name ='张三' AND addr = '北京市'语句,如果name有索引而addr没索引,那么SQL语句不会使用索引。

多字段索引,违反最佳左前缀原则。例如,student表如果建立了(name,addr,age)这样的索引,WHERE后的第一个查询条件一定要是name,索引才会生效。

2、<>、NOT、in、not exists

当查询条件为等值或范围查询时,索引可以根据查询条件去找对应的条目。否则,索引定位困难(结合我们查字典的例子去理解),执行计划此时可能更倾向于全表扫描,这类的查询条件有:<>、NOT、in、not exists

3、查询条件中使用OR

如果条件中有or,即使其中有条件带索引也不会使用(因此SQL语句中要尽量避免使用OR)。要想使用OR,又想让索引生效,只能将OR条件中的每个列都加上索引。

4、查询条件使用LIKE通配符

SQL语句中,使用后置通配符会走索引,例如查询姓张的学生(SELECT * FROM student WHERE name LIKE '张%'),而前置通配符(SELECT * FROM student WHERE name LIKE '%东')会导致索引失效而进行全表扫描。

5、索引列上做操作(计算,函数,(自动或者手动)类型装换)

有以下几种例子:

在索引列上使用函数:例如select * from student where upper(name)='ZHANGFEI';会导致索引失效,而select * from student where name=upper('ZHANGFEI');是会使用索引的。

在索引列上计算:例如select * from student where age-1=17;

6、在索引列上使用mysql的内置函数,索引失效

例如,SELECT * FROM student WHERE create_time

7、索引列数据类型不匹配

例如,如果age字段有索引且类型为字符串(一般不会这么定义,此处只是举例)但条件值为非字符串,索引失效,例如SELECT * FROM student WHERE age=18会导致索引失效。

8、索引列使用IS NOT NULL或者IS NULL可能会导致无法使用索引

B-tree索引IS NULL不会使用索引,IS NOT NULL会使用,位图索引IS NULL、IS NOT NULL都会使用索引。

最后,对索引的使用做一个总结吧:

索引有利于查询,但不能随意加索引,因为索引不仅会占空间,而且需要在写库时进行维护。

如果多个字段常常需要一起查询,那么在这几个字段上建立联合索引是个好办法,同时注意最左匹配原则。

不要在重复度很高的字段上加索引,例如性别。

1.3.5 索引分类

1.3.5.1 普通索引

--建表,建一个表空间TS1,在表的一列上建普通索引,并指定索引的物理存储(初始簇大小为 50,下次分配簇数目为50,所在表空间为TS1):

create table t_28(c1 int, c2 char(10), c3 date);

create tablespace TS1 DATAFILE 'TS1.DBF' SIZE 32;create index common_idx on t_28(c1) STORAGE ( INITIA50, NEXT 50, ON TS1);

--通过系统表查询索引的ID:

select name, id from sysobjects where name='COMMON_IDX' and subtype$='INDEX';--使用第2步查出的索引id查看普通索引的定义:

SELECT indexdef(33555445,1);

--删除索引:

drop index common_idx;

1.3.5.2 聚集索引

当建表语句未指定聚集索引键时,DM8 的默认聚集索引键是 ROWID。若指定索引键,表中数据都会根据指定索引键排序。

建表后,DM8 也可以用创建新聚集索引的方式来重建表数据,并按新的聚集索引排序。例如,可以对 emp 表以 ename 列新建聚集索引。

CREATE CLUSTER INDEX clu_emp_name ON emp(ename);

新建聚集索引会重建这个表以及其所有索引,包括二级索引、函数索引,是一个代价非常大的操作。

因此,最好在建表时就确定聚集索引键,或在表中数据比较少时新建聚集索引,而尽量不要对数据量非常大的表建立聚集索引。

创建聚集索引的约束条件:

1.每张表中只允许有一个聚集索引,如果之前已经指定过 CLUSTER INDEX 或者指定了 CLUSTER PK,则用户新建立 CLUSTER INDEX 时系统会自动删除原先的聚集索引。但如果新建聚集索引时指定的创建方式(列,顺序)和之前的聚集索引一样,则会报错;

2.指定 CLUSTER INDEX 操作需要重建表上的所有索引,包括 PK 索引;

3.删除聚集索引时,缺省以 ROWID 排序,自动重建所有索引;

4.若聚集索引是默认的 ROWID 索引,不允许删除;

5.聚集索引不能应用到函数索引中;

6.不能在列存储表上新建/删除聚集索引;

7.建聚集索引语句不能含有 partition_clause 子句;

8.在临时表上增删索引会使当前会话上临时 b 树数据丢失。

--创建表,在第一列上建聚集索引:

create table t_29(c1 int, c2 char(10), c3 date);

create or replace cluster index clu_idx on t_29(c1);

select name, id from sysobjects where name='CLU_IDX' and subtype$='INDEX';

--使用第2步查出的索引id查看普通索引的定义:

SELECT indexdef(33555463,1);

drop index clu_idx;

1.3.5.3 唯一索引

索引可以是唯一的或非唯一的。唯一索引可以保证表上不会有两行数据在键列上具有相同的值,非唯一索引不会在键列上施加这个限制。可用 CREATE UNIQUE INDEX 语句来创建唯一索引,如下例子创建一个唯一索引:

CREATE UNIQUE INDEX dept_unique_index ON dept (dname) STORAGE (ON users);

用户可以在希望的列上定义 UNIQUE 约束,DM8 通过自动地在唯一键上定义一个唯一索引来保证 UNIQUE 完整性约束。

create table t_30(c1 int, c2 char(10), c3 date);

create or replace unique index uni_idx on t_30(c1);

--查询索引id:

select name, id from sysobjects where name='UNI_IDX' and subtype$='INDEX';

--使用第2步查出的索引id查看普通索引的定义:

SELECT indexdef(33555467,1);

drop index uni_idx;

1.3.5.4 位图索引

位图索引主要针对含有大量相同值的列而创建。位图索引被广泛引用到数据仓库中,创建方式和普通索引一致,对低基数(不同的值很少)的列创建位图索引,能够有效提高基于该列的查询效率。且执行查询语句的 where 子句中带有 AND 和 OR 谓词时,效率更加明显。

位图索引具有以下约束:

1.支持普通表、堆表和水平分区表创建位图索引;

2.不支持对大字段创建位图索引;

3.不支持对计算表达式列创建位图索引;

4.不支持在 UNIQUE 列和 PRIMARY KEY 上创建位图索引;

5.不支持对存在 CLUSTER KEY 的表创建位图索引;

6.仅支持单列或者不超过 63 个组合列上创建位图索引;

7.MPP 环境下不支持位图索引的创建;

8.不支持快速装载建含有位图索引的表;

9.不支持全局位图索引;

10.包含位图索引的表不支持并发的插入、删除和更新操作。

姓名(Name)性别(Gender)婚姻状况(Marital)
张三已婚
李四已婚
王五未婚
赵六离婚

位图索引创建了之后,生成是位图数据可以这么理解,比如,男女两种,然后一共八条数据,那么就生产两个字符串,一个代表男,一个代表女,字符串长度为数据的总数量,字符串的值:第一位(如果第一条数据是男那么就是1,如果不是就0),第二位,第三位,往后都是这样。由此就生成了长度为总数量,只包含01的字符串,通过这个字符串就能知道第几条数据是男,第几条不是男,同理,另外一条代表女的字符串也一样,是女的就1,不是女的就0。

如果用户查询的列的基数非常的小, 即只有的几个固定值,如性别、婚姻状况、行政区等等。要为这些基数值比较小的列建索引,就需要建立位图索引。

对于性别这个列,位图索引形成两个向量,男向量为10100...,向量的每一位表示该行是否是男,如果是则位1,否为0,同理,女向量位01011。

RowId12345...
10100
01011

对于婚姻状况这一列,位图索引生成三个向量,已婚为11000...,未婚为00100...,离婚为00010...。

RowId12345...
已婚11000
未婚00101
离婚00010

当我们使用查询语句“select * from table where Gender=‘男’ and Marital=“未婚”;”的时候 首先取出男向量10100...,然后取出未婚向量00100...,将两个向量做and操作,这时生成新向量00100...,可以发现第三位为1,表示该表的第三行数据就是我们需要查询的结果。

RowId12345
10100
and
未婚00101
结果00100

3.**位图索引的适用条件**

上面讲了,位图索引适合只有几个固定值的列,如性别、婚姻状况、行政区等等,而身份证号这种类型不适合用位图索引。

此外,位图索引适合静态数据,而不适合索引频繁更新的列。举个例子,有这样一个字段busy,记录各个机器的繁忙与否,当机器忙碌时,busy为1,当机器不忙碌时,busy为0。

这个时候有人会说使用位图索引,因为busy只有两个值。好,我们使用位图索引索引busy字段!假设用户A使用update更新某个机器的busy值,比如update table set table.busy=1 where rowid=100;,但还没有commit,而用户B也使用update更新另一个机器的busy值,update table set table.busy=1 where rowid=12; 这个时候用户B怎么也更新不了,需要等待用户A commit。

原因:用户A更新了某个机器的busy值为1,会导致所有busy为1的机器的位图向量发生改变,因此数据库会将busy=1的所有行锁定,只有commit之后才解锁。

create table t_31(c1 int, c2 char(10), c3 date);

create or replace bitmap index bm_idx on t_31(c1);

--查询索引id:

select name, id from sysobjects where name='BM_IDX' and subtype$='INDEX';

--使用第2步查出的索引id查看普通索引的定义:

SELECT indexdef(33555469,1);

drop index bm_idx;

1.3.5.5 位图连接索引

位图连接索引是针对两个或者多个表的连接而建立的位图索引,同时保存了连接的位图结果。对于索引列中的每一个值,位图连接索引在索引表中保存了对应行的 ROWID。

2.用于连接的列必须是维度表中的主键或存在唯一约束;如果是复合主键,则必须使用复合主键中的所有列;

3.当多个事务同时使用位图连接索引时,同一时间只允许更新一个表;

4.连接索引创建时,基表只允许出现一次;

5.不允许对存在 cluster key 的表创建位图连接索引;

7.不支持对位图连接索引所在事实表和维度表的备份还原,不支持位图连接索引表的表级备份还原;

11.仅支持普通表、堆表和 HUGE 表;

12.WHERE 条件只能是列与列之间的等值连接,并且必须含有所有表;

13.事实表上聚集索引和位图连接索引不能同时存在;

14.不支持对含有位图连接索引的表中的数据执行 DML,如需要执行 DML,则先删除该索引;

15.含有位图连接索引的表不支持下列 DDL操作:删除、修改表约束,删除、修改列,更改表名。另外,含位图连接索引的堆表不支持添加列操作;

16.不允许对含有位图连接索引的表并发操作;

--创建基表:

create table t_32(c1 int not CLUSTER primary key, c2 char(10), c3 date);

insert into t_32 values(1, 'a','2012-01-02');

insert into t_32 values(2, 'b','2012-02-02');

insert into t_32 values(3, 'c','2012-03-02');

--创建基表:

create table t_33(t1 int unique, t2 char(10), t3 date);

insert into t_33 values(1, 'aa','2012-01-02');

insert into t_33 values(11, 'a','2012-01-02');

--创建位图连接索引:

create or replace bitmap index b_idx on t_32(c1) from t_32, t_33 where t_32.c1=t_33.t1;

select * from t_32, t_33 where t_32.c1=t_33.t1 and t_33.t3='2012-01-02';

--删除索引:

drop index b_idx;

1.3.5.6 函数索引

--创建函数索引,执行查询计划:

create table t_34(c1 int, c2 int, c3 char(20), c4 date, c6 varchar(30));

create index idx on t_34(c1+c2);

explain select * from t_34 where c1+c2<20;

--不创建函数索引,执行查询计划:

create table t_35(c1 int, c2 int, c3 char(20), c4 date, c6 varchar(30));

explain select * from t_35 where c1+c2<20;

drop index idx;

1.3.5.7 分区索引

create table t_36(c1 int , c2 char(10),c3 date)storage(branch(2,4));

create index t_global_idx1 on t_36(c3) globapartition by list(c3)

(

partition global1 values ('2009-12-10'),

partition global1 values ('2010-12-10'),

partition global1 values ('2011-12-10'),

partition global1 values ('2012-12-10')

);

drop index t_global_idx1;

create index t_global_idx2 on t_36(c1) globapartition by range(c1)

(

partition g_p1 values less than(10),

partition g_p2 values less than(maxvalue)

);

drop index t_global_idx2;

create index t_global_idx3 on t_36(c1) globapartition by hash(c1)

(

partition g_h1 ,

partition g_h2 );

drop index t_global_idx3;

create index t_global_idx4 on t_36(c1);

drop index t_global_idx4;

1.3.5.8 全文索引

create table t_37(c1 int, c2 char(50));

insert into t_37 values(1,'湖北省武汉市洪山区');

insert into t_37 values(2,'湖北省武汉市青山区');

insert into t_37 values(3,'湖北省武汉市汉阳区');

insert into t_37 values(4,'湖北省武汉市武昌区');

insert into t_37 values(5,'湖北省武汉市硚口区');

--创建全文索引:

create context index c_idx on t_37(c2) lexer chinese_lexer;

--更新全文索引:

alter context index c_idx on t_37 rebuild;

--查询全文索引:

select * from t_37 where contains (c2,'青山区');

--删除全文索引:

drop context index c_idx on t_37;

1.4 视图

从系统实现的角度讲,视图是从一个或几个基表(或视图)导出的表,但它是一个虚表,即数据字典中只存放视图的定义(由视图名和查询语句组成),而不存放对应的数据,这些数据仍存放在原来的基表中。

当对一个视图进行查询时,视图将查询其对应的基表,并且将所查询的结果以视图所规定的格式和次序进行返回。因此当基表中的数据发生变化时,从视图中查询出的数据也随之改变了。

DM提供了一些以"V$"开头的视图来供用户了解当前服务器的使用情况。

查看视图定义:

calSP_VIEWDEF('SYS','ALL_TABLES');

--建表:

create table student(id int, name char(10), sex char(2), bithdate date, house_place varchar, classid char(10));

insert into student values(10001, '张强','男', '1990-12-1','湖北省武汉市武昌区', '1-3');

insert into student values(10002, '李凯','男', '1983-8-1','湖北省武汉市江汉区','1-2');

insert into student values(10003, '王可','女', '1980-11-7','湖北省武汉市青山区','2-1');

insert into student values(10004, '许畅','女', '1993-7-13','湖北省武汉市青山区','3-2');

select * from student;

--创建视图和查询视图:

create or replace view person as select id, name, house_place from student where sex='女';

select * from person;

alter table student add column (education varchar);

select * from student;

select * from person;

select SF_VIEW_EXPIRED('SYSDBA','PERSON');

--重编译视图:

alter view person compile;

select * from person;

一个视图依赖于其基表或视图,如果基表定义发生改变,如增删一列,或者视图的相关权限发生改变,可能导致视图无法使用。在这种情况下,可对视图重新编译,检查视图的合法性。

1.5 物化视图

物化视图是从一个或几个基表导出的表,同视图相比,它存储了导出表的真实数据。

当基表中的数据发生变化时,物化视图所存储的数据将变得陈旧,用户可以通过手动刷新或自动刷新来对数据进行同步。

--建表和物化视图:

create table t_1(c1 int, c2 char(10), c3 varchar(20), c4 date);

create table t_2(t1 int, t2 char(10), t3 varchar(20), t4 date);

create materialized view m_view(v1, v2, v3, v4) build immediate refresh force enable query rewrite as select t_1.c1 as v1, t_1.c2 as v2, t_1.c3 as v3, t_2.t4 as v4 from t_1, t_2 where t_1.c1=t_2.t1;

select * from m_view;

--禁止物化视图用于查询改写。

alter materialized view m_view disable query rewrite;

--更新物化视图

refresh materialized view m_view complete;

drop materialized view m_view;

drop table t_1; drop table t_2;

1.6 存储过程和函数

存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQ语句集,它存储在数据库中,一次编译后永久有效,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象。在数据量特别庞大的情况下利用存储过程能达到倍速的效率提升。

函数与存储过程相似,也是数据库中存储的已命名PL-SQL程序块。函数的主要特征是它必须有一个返回值。通过return来指定函数的返回类型。在函数的任何地方可以通过return expression语句从函数返回,返回类型必须和声明的返回类型一致。

函数和存储过程的区别:

1、存储过程用户在数据库中完成特定操作或者任务(如插入,删除等),函数用于返回特定的数据。

2、存储过程声明用procedure,函数用function。

3、存储过程不需要返回类型,函数必须要返回类型。

4、存储过程可作为独立的pl-sql执行,函数不能作为独立的plsql执行,必须作为表达式的一部分。

5、存储过程只能通过out和in/out来返回值,函数除了可以使用out,in/out以外,还可以使用return返回值。

6、sql语句(DML或SELECT)中不可用调用存储过程,而函数可以。

7、如果需要返回多个值和不返回值,就使用存储过程;如果只需要返回一个值,就使用函数。

create table t_38(c1 int, c2 char(10), c3 varchar(20));

insert into t_38 values(1,'a','aaaaa');

insert into t_38 values(2,'b','bbbbb');

insert into t_38 values(3,'c','ccccc');

--创建不带参数的存储过程:

create or replace procedure proc1 is totaint;

begin

select count(*) into totafrom t_38;

print total;

end;

calproc1;

drop procedure proc1;

drop table t_38;

create table t_38(c1 int, c2 char(10), c3 varchar(20));

--创建带参数的存储过程:

create or replace procedure proc1(i in int)

as j int;totaint;

begin

for j in 1 ..i loop

insert into t_38 values(j,3,'aaaaa');

select count(*) into totafrom t_38;

print total;

end loop;

end;

proc1(10);

drop procedure proc1;

--创建一个存储函数f1:

create or replace function f1 (b1 in varchar2)

return varchar2 is

begin print('elapsed time seconds.'||b1);

return b1||'Hello';

end ;

--disq里set serverout on; 能显示elapsed ……

select f1('zhangsan');

--重新创建存储函数f1:

create or replace function f1 for calculate

return int

is

begin

return 1;

end;

alter function f1 compile;

create table t_39(c1 int,c2 int default f1());

insert into t_39(c1) values (1);

select * from t_39;

1.7 序列

序列(sequence)是 DM 数据库中的数据库实体之一。通过使用序列,多个用户可以产生和使用一组不重复的有序整数值。比如可以用序列来自动地生成主关键字值。序列通过提供唯一数值的顺序表来简化程序设计工作。当一个序列第一次被查询调用时,它将返回一个预定值,该预定值就是在创建序列时所指定的初始值。

在随后的每一次查询中,序列将产生一个按其指定的增量增长的值。增量可以是任意的正整数或负整数,但不能为 0

一旦序列生成,用户就可以在 SQ语句中用以下伪列来存取序列的值:1.CURRVA返回当前的序列值;2.NEXTVA如果为升序序列,序列值增加并返回增加后的值;如果为降序序列,序列值减少并返回减少后的值。

select "TEST"."TEST_SEQ".NEXTVAnex;

insert TEST.TABLE_2 VALUES( "TEST"."TEST_SEQ".NEXTVAL);

--查询和插入nextval都会使当前值变化。

select "TEST"."TEST_SEQ".CURRVAcurr;

使用一个序列时,不保证将生成一串连续不断递增的值。例如,如果查询一个序列的下一个值供 insert 使用,则该查询是能使用这个序列值的唯一会话。如果未能提交事务处理,则序列值就不被插入表中,以后的 insert 将继续使用该序列随后的值。

序列在对编号的使用上具有很大用处,如果想对表建立一个列专门用来表示编号,如订单号,这样就可以使用序列,依次递增生成,用户不需进行特殊管理,这给用户带来了很大方便。

报错:序列当前值尚未在此会话中定义

NEXTVAL可以单独使用在sql语句中;而CURRVAL在没有使用NEXTVAL的时候使用的话就会报错(尚未在此会话中定义)。

解决:在执行CURRVAL之前需要先执行NEXTVAL:

select "TEST"."TSEST_SEQ".NEXTVAL;

--创建表和序列:

create table t1(c1 int,c2 varchar(10));

create sequence seq1 start with 1 increment by 10;

--将序列的前两个值插入到表中:

insert into t1 values(seq1.nextval,'test');

insert into t1 values(seq1.nextval,'test2');

--查询表数据:

select * from t1;

--获取序列的当前值:

select seq1.currval;

--删除序列和表:

drop SEQUENCE seq1;

drop table t1;

1.8 触发器

触发器(TRIGGER)定义当某些与数据库有关的事件发生时,数据库应该采取的操作

触发器是一种特殊的存储过程,它在创建后就存储在数据库中。触发器的特殊性在于它是建立在某个具体的表或视图之上的,或者是建立在各种事件前后的,而且是自动激发执行的,如果用户在这个表上执行了某个 DM操作(INSERT、DELETE、UPDATE),触发器就被激发执行。

触发器常用于自动完成一些数据库的维护工作。例如,触发器可以具有以下功能:1.可以对表自动进行复杂的安全性、完整性检查;2.可以在对表进行 DM操作之前或者之后进行其它处理;3.进行审计,可以对表上的操作进行跟踪;4.实现不同节点间数据库的同步更新

DM 提供了三种类型的触发器:1.表级触发器:基于表中的数据进行触发;2.事件触发器:基于特定系统事件进行触发;3.时间触发器:基于时间而进行触发。

在 DM 的数据守护环境下,备库上定义的触发器是不会被触发的。

ALTER TRIGGER "SYSDBA"."triggerlogin" DISABLE;

ALTER TRIGGER "SYSDBA"."triggerlogin" enable;

1.8.1 before

--执行以下sql语句建表:

DROP TABLE if exists T;

CREATE TABLE T(A INT PRIMARY KEY, B VARCHAR(10));

INSERT INTO T VALUES(1,'AAA');

INSERT INTO T VALUES(2,'BBB');

INSERT INTO T VALUES(3,'BBB');

--建BEFORE语句级触发器:

CREATE OR REPLACE TRIGGER TRG_BEF

BEFORE INSERT ON T

BEGIN

PRINT('INSERTING...');

END;

--检验触发:

INSERT INTO T VALUES(3,'BBB');

1.8.2 after

CREATE TABLE T(A INT PRIMARY KEY, B VARCHAR(10));

INSERT INTO T VALUES(1,'AAA');

INSERT INTO T VALUES(2,'BBB');

INSERT INTO T VALUES(3,'BBB');

--建AFTER语句级触发器:

CREATE OR REPLACE TRIGGER TRG_AFT

AFTER INSERT ON T

BEGIN

PRINT('inserted');

END;

--检验触发:

INSERT INTO T VALUES(3,'BBB');

INSERT INTO T VALUES(4,'BBB');

1.8.3 元组级/行级

--执行以下sql语句建表、建触发器:

CREATE TABLE T01_TRI_00120(C1 INT PRIMARY KEY);

CREATE TABLE T02_TRI_00120(C1 INT,FOREIGN KEY(C1) REFERENCES T01_TRI_00120(C1));

CREATE TRIGGER TRI01_TRI_00120 BEFORE INSERT ON T02_TRI_00120

FOR EACH ROW --行级(元组级)触发器

BEGIN

INSERT INTO T01_TRI_00120 VALUES(:NEW.C1);

END TRI01_TRI_00120;

--检验触发:

INSERT INTO T02_TRI_00120 VALUES(2); --insert之前触发了触发器,向引用表中插入了数据,否则无法插入数据

SELECT * FROM T01_TRI_00120;

1.8.4 ddl

CREATE OR REPLACE TRIGGER TRG_DDL

BEFORE CREATE

on SYSDBA.schema

BEGIN

PRINT('a');

END;

CREATE TABLE T(A INT PRIMARY KEY, B VARCHAR(10));

drop table t;

drop TRIGGER TRG_DDL;

1.8.5 系统事件

create table login_t(C1 int);

insert into LOGIN_T VALUES(1);

create trigger "triggerlogin"

after LOGIN

on database

BEGIN

/触发器体/

print('login database');

insert into LOGIN_T VALUES(2);

END;

--触发器体里面加上向某各表里插入数据的操作,更好验证。

--先开了一个disql,set serverout on ,然后再开一个disql,连接上数据库之后

--前一个disql里没有打印出login database.

--创建一个新用户U1,在开了serverout的disql上conn U1/Dameng123之后,

--conn登录U1成功,表login_t里多了一条数据,但仍然没有打印login database。

--login登录和管理工具新建查询也是一样,表里多了数据,没打印语句。

--管理工具点击注册连接并登录成功后,会自动新建一个查询

--因此相当于两个连接,多了两条数据。

1.8.6 时间触发器

--在每个月的第15 天,从14:20 到14:30,每隔一分钟就打印一个“Hello World”

--并且向表time_t中插入一条数据。

--触发器体里面加上向某各表里插入数据的操作,更好验证。

create table time_t(C1 int);

insert into TIME_T values(1);

CREATE OR REPLACE TRIGGER timer3

AFTER TIMER ON DATABASE

FOR EACH 1 MONTH DAY 15

FROM TIME '14:20' TO TIME '14:30' FOR EACH 1 MINUTE

str VARCHAR;

BEGIN

PRINT 'HELLO WORLD';

insert into TIME_T values(2);

END;

1.9 包package

csdn上搜的:

包的作用:包可以将任何出现在块声明的语句(过程,函数,游标,游标,类型,变量)放于包中,相当于一个容器.将声明语句放入包中的好处是:用户可以从其他PL/SQL块中对其进行引用,因此包为PL/SQL提供了全程变量.

包分为两部分:包头和包体.

定义包头应当遵循以下原则:

1)包元素位置可以任意安排.然而在声明部分,对象必须在引用前进行声明.

2)包头可以不对任何类型的元素进行说明.例如,包头可以只带过程和函数说明语句,而不声明任何异常和类型.

3)对过程和函数的任何声明都必须只对子程序和其参数进行描述,不能有任何代码的说明,代码的实现只能在包体中出现.它不同于块声明,在块声明中,过程和函数的代码可同时出现在声明部分.

包体:

语法格式:

CREATE OR REPLACE PACKAGE BODY package_name/包名必须与包头的包名一致/

IS | AS pl/sql_package_body /游标,函数,过程的具体定义/

包体是与包头相互独立的,包体只能在包头完成编译后才能进行编译.包体中带有包头中描述的子程序的具体实现的代码段.除此之外,包体还可以包括具有包体人全句属性的附加声明部分,但这些附加声明对于包头是不见的

重载:包中的函数和过程可以重载

以下条件不能重载:

1.如果两个子程序的参数仅在名称和类型上不同,这两个程序不能重载.

PROCEDURE overloadME(p_theparameterIN number);

PROCEDURE overloadME(p_theparameterOUT number);

IN ,OUT为参数类型,number为数据类型.两个过程仅在类型上不同时不能重载.

2.不能根据两个函数的返回类型对其重载

如:

FUNCTION overloadMeETooRETURN DATE;

FUNCTION overloadMeETooRETURN NUMER;

3.重载子程序的参数的类族必须不同,例如,由于CHAR和VARCHAR2属性同一类族,所以不能重载.

PROCEDURE overloadME(p_theparameterIN char);

PROCEDURE overloadME(p_theparameterIN varchar2);

4.打包子程序也可以重载

5.包的初始化.

当第一次调用打包子程序时,该包将进行初始化.也就是说,将该包从硬盘中读入到内存,并启用调用的子程序的编译代码.这时,系统为该包中定义的所有变量分配内存单元.每个会话都有打其打开包变量的副本,以确保执行同一个包子程序的两个会话使用不同的内存单元.

在大多数情况下,初始化代码要在包第一次初始化时运行.为了实现这一功能,可以在包体中的所有对象之后加入一个初始化代码.

--创建表

CREATE TABLE PERSON(ID INT IDENTITY, NAME VARCHAR(100), CITY VARCHAR(100));

INSERT INTO PERSON(NAME, CITY) VALUES('TOM','武汉');

--创建包规范

CREATE OR REPLACE PACKAGE PERSONPACKAGE IS

E_NOPERSON EXCEPTION;

PERSONCOUNT INT;

PCUR CURSOR;

PROCEDURE ADDPERSON(PNAME VARCHAR(100), PCITY VARCHAR(100));

PROCEDURE REMOVEPERSON(PNAME VARCHAR(100), PCITY VARCHAR(100));

PROCEDURE REMOVEPERSON(PID INT);

FUNCTION GETPERSONCOUNT RETURN INT;

PROCEDURE PERSONLIST;

END PERSONPACKAGE;

--创建包体

CREATE OR REPLACE PACKAGE BODY PERSONPACKAGE AS

PROCEDURE ADDPERSON(PNAME VARCHAR(100), PCITY VARCHAR(100)) AS

BEGIN

INSERT INTO PERSON(NAME, CITY) VALUES(PNAME, PCITY);

PERSONCOUNT = PERSONCOUNT + SQL%ROWCOUNT;

END ADDPERSON;

PROCEDURE REMOVEPERSON(PNAME VARCHAR(100), PCITY VARCHAR(100)) AS

BEGIN

DELETE FROM PERSON WHERE NAME LIKE PNAME AND CITY LIKE PCITY;

PERSONCOUNT = PERSONCOUNT - SQL%ROWCOUNT;

END REMOVEPERSON;

PROCEDURE REMOVEPERSON(PID INT) AS

BEGIN

DELETE FROM PERSON WHERE ID = PID;

PERSONCOUNT = PERSONCOUNT - SQL%ROWCOUNT;

END REMOVEPERSON;

FUNCTION GETPERSONCOUNT RETURN INT AS

BEGIN

RETURN PERSONCOUNT;

END GETPERSONCOUNT;

PROCEDURE PERSONLIST AS

DECLARE

V_ID INT;

V_NAME VARCHAR(100);

V_CITY VARCHAR(100);

BEGIN

IF PERSONCOUNT = 0 THEN

RAISE E_NOPERSON;

END IF;

OPEN PCUR FOR SELECT ID, NAME, CITY FROM PERSON;

LOOP

FETCH PCUR INTO V_ID,V_NAME,V_CITY;

EXIT WHEN PCUR%NOTFOUND;

PRINT ('NO.' + (CAST (V_ID AS VARCHAR(100))) + ' ' + V_NAME + '????' + V_CITY );

END LOOP;

CLOSE PCUR;

END PERSONLIST;

BEGIN

SELECT COUNT(*) INTO PERSONCOUNT FROM PERSON;

END PERSONPACKAGE;

--调用包:

CAL PERSONPACKAGE.ADDPERSON ('BLACK', '南京');

--查询表数据:

SELECT * FROM PERSON;

--删除包主体和包规范:

drop PACKAGE BODY PERSONPACKAGE;

drop PACKAGE PERSONPACKAGE;

1.10 类class

--类的声明:创建类头:

create class mycls

as

type rec_type is record (c1 int, c2 int); --类型声明

id int; -- 成员变量

r rec_type; --成员变量

function f1(a int, b int) return rec_type; --成员函数

function mycls(id int , r_c1 int, r_c2 int) return mycls;

-- 用户自定义构造函数

end;

/

--类的实现:创建类体:

create or replace class body mycls

as

function f1(a int, b int) return rec_type

as

begin

r.c1 = a;

r.c2 = b;

return r;

end;

function mycls(id int, r_c1 int, r_c2 int) return mycls

as

begin

this.id = id; -- 可以使用this来访问自身的成员

r.c1 = r_c1; --this也可以省略

r.c2 = r_c2;

return this; -- 使用return this返回本对象

end;

end;

/

--类的删除:

drop class mycls;

1.11 同义词

同义词相当于模式对象的别名,起着连接数据库模式对象和应用程序的作用。假如模式对象需要更换或者修改,则不用修改应用程序而直接修改同义词就可以了。同义词是用来实现下列用途的数据库对象:1.为可以存在于本地或远程服务器上的其他数据库对象(称为基础对象)提供备用名称;2.提供抽象层以免客户端应用程序对基础对象的名称或位置进行更改。

同义词的好处在于用户可能需要某些对象在不同的场合采用不用的名字,使其适合不同人群的应用环境。例如,创建表 product,如果客户不认识这个英文词,这时可以增加同义词,命名"产品",这样客户就有较直观的观念,一目了然。

1.11.1 非公共同义词

--管理员新建一个用户,并授予建表、建同义词权限:

create user "A" identified by "aaa123456";

grant create table to A;

grant create synonym to A;

--用户A新建一个到数据库的连接

--用户A在A 模式下建立表T1:

CREATE TABLE "T1" ("ID" INTEGER, "NAME" VARCHAR(50), PRIMARY KEY("ID"));

INSERT INTO "A"."T1" ("ID", "NAME") VALUES (1, '张三');

INSERT INTO "A"."T1" ("ID", "NAME") VALUES (2, '李四');

--用户A对A 模式下的表T1 创建同义词(属于非公共同义词):

CREATE SYNONYM S1 FOR A.T1;

--用户A通过同义词查询表T1的数据:

SELECT * FROM A.S1;

--用户SYSDBA通过同义词查询表T1的数据:

SELECT * FROM A.S1;

create user "B" identified by "aaa123456";

grant SELECT on "A"."T1" to "B";

--普通用户通过同义词查询表T1的数据,成功

SELECT * FROM A.S1;

--SYSDBA收回权限

revoke SELECT on "A"."T1" from "B" cascade;

--普通用户通过同义词查询表T1的数据,应该不成功

SELECT * FROM A.S1;

1.12 域

什么是域?

域是一种在关系当中列所允许值的表达式。当定义一个表的时候,会为每个列指派一种数据类型(如字符型或者是整型),这些数据类型提供了一个广泛域。数据库管理系统将不会保存那些违背约束的数据信息。

SQL-92标准引入了用户自定义域的概念。要使用用户自定义域,必须先创建该自定义域。

--创建域:

CREATE DOMAIN DA INT CHECK (VALUE < 100);

--在表定义中使用1中创建的域:

CREATE TABLE T(ID DA);

--插入数据检查使用了域的列定义是否有效:

INSERT INTO T VALUES(99);

INSERT INTO T VALUES(101);

--域的删除:需先删除使用了域的表:

DROP TABLE T;

DROP DOMAIN DA;

1.13 类型type(自定义类型)

create or replace type person as object(name varchar(10) , sex char(2), birthdate date);

create table t_person of person;

2. 非模式对象

2.1 角色

--SYSDBA创建一个角色

CREATE ROLE role1;

--SYSDBA建测试表和用户:

--DROP TABLE T1;

CREATE TABLE T1(C1 INT,C2 VARCHAR(20));

INSERT INTO T1 VALUES(1,'TEST1'),(2,'TEST2');

CREATE USER USER1 IDENTIFIED BY AAABBBCCC;

--SYSDBA向该角色授予若干数据库权限:

GRANT SELECT ON t1 TO role1;

GRANT CREATE TABLE TO role1;

--将角色role1分配给用户user1:

GRANT role1 TO user1 WITH ADMIN OPTION;

--USER1用户登录系统执行:

SELECT * FROM SYSDBA.T1;

CREATE TABLE T2(C1 INT,C2 VARCHAR(20));

--SYSDBA从角色中删除SELECT ON t1权限:

REVOKE SELECT ON t1 FROM role1;

--user1再执行查询表t1:

SELECT * FROM SYSDBA.T1;

--SYSDBA禁用角色role1:

SP_SET_ROLE('ROLE1', 0);

--USER1用户执行建表:

CREATE TABLE T3(C1 INT,C2 VARCHAR(20));

--SYSDBA启用角色role1:

SP_SET_ROLE('ROLE1', 1);

--USER1用户执行建表:

CREATE TABLE T3(C1 INT,C2 VARCHAR(20));

--清除测试环境:

DROP TABLE T1;

DROP USER USER1 CASCADE;

DROP ROLE role1;

2.2 用户

2.3 公共同义词

--建表:

create table t1(c1 int,c2 varchar(10));

insert into t1 values(1,'test');

--建表T1的同义词:

CREATE OR REPLACE PUBLIC SYNONYM S1 FOR SYSDBA.T1;

--通过同义词来查询表数据:

select * from s1;

--删除公共同义词和表:

drop PUBLIC SYNONYM S1;

drop table t1;

2.4 表空间

表空间有联机和脱机两种状态。

系统表空间、回滚表空间、重做日志表空间()和临时文件表空间不允许脱机,不允许修改数据缓冲区。

在DM数据库中,表空间由一个或者多个数据文件组成。DM数据库中的所有对象在逻辑上都存放在表空间中,而物理上都存储在所属表空间的数据文件中。

在创建DM数据库时,会自动创建5个表空间:SYSTEM表空间、ROLL表空间、MAIN表空间、TEMP表空间和HMAIN表空间。

1.SYSTEM表空间存放了有关DM数据库的字典信息,用户不能在SYSTEM表空间创建表和索引。

2.ROLL表空间完全由DM数据库自动维护,用户无需干预。该表空间用来存放事务运行过程中执行DML操作之前的值,从而为访问该表的其他用户提供表数据的读一致性视图。

3.MAIN表空间在初始化库的时候,就会自动创建一个大小为128M的数据文件MAIN.DBF。在创建用户时,如果没有指定默认表空间,则系统自动指定MAIN表空间为用户默认的表空间。

4.TEMP表空间完全由DM数据库自动维护。当用户的SQL语句需要磁盘空间来完成某个操作时,DM数据库会从TEMP表空间分配临时段。如创建索引、无法在内存中完成的排序操作、SQL语句中间结果集以及用户创建的临时表等都会使用到TEMP表空间。

5.HMAIN表空间属于HTS表空间,完全由DM数据库自动维护,用户无需干涉。当用户在创建HUGE表时,未指定HTS表空间的情况下,充当默认HTS表空间。

SYS、SYSSSO、SYSAUDITOR系统用户,默认的用户表空间是SYSTEM,

SYSDBA的默认表空间为MAIN,新创建的用户如果没有指定默认表空间,则系统自动指定MAIN表空间为用户默认的表空间。

如果用户在创建表的时候指定了存储表空间A,并且和当前用户的默认表空间B不一致时,表存储在用户指定的表空间A中,并且默认情况下,在这张表上面建立的索引也将存储在A中,但是用户的默认表空间是不变的,仍为B。

--查看普通表空间信息:

select * from V$tablespace;

--查看huge表空间信息:

select * from V$huge_tablespace;

--查询表空间与数据文件对应关系

SELECT ts.NAME, df.PATH FROM V$TABLESPACE AS ts, V$DATAFILE AS df WHERE ts.ID= df.GROUP_ID;

修改回滚表空间的数据文件路径的时候:

回滚文件的路径记录在控制文件里面,可以使用 dmctlcvt 工具在 DM 服务器关闭的状态下对控制文件进行修改。使用 dmctlcvt 工具将控制文件转换为文本文件,编辑文本文件中要修改的文件的路径后再使用 dmctlcvt 工具将文本文件转换为控制文件即可。

首先转换控制文件到文本文件:

dmctlcvt c2t D:\dm.ctD:\ctl.txt

编辑 ctl.txt 文本文件中 fil_path=d:\roll.dbf 为 fil_path=e:\ roll.dbf,

保存文本文件。复制 d:\roll.dbf 文件为 e:\ roll.dbf。

最后转换文本文件到控制文件:

dmctlcvt t2c D:\ctl.txt D:\dm.ctl

这种修改文件路径的方法也可用于重做日志文件,系统表空间文件等路径的修改。

只能只用dmctlcvt的原因:

因为使用alter tablespace修改表空间的数据文件路径时,必须将表空间设置为脱机状态:alter tablespace offline。但是系统表空间、回滚表空间、重做日志表空间和临时文件表空间不允许脱机。

2.5 类型别名

--初始化类型别名运行环境:

CALSP_INIT_DTYPE_SYS(1);

--执行以下脚本创建数据类型别名:

CALSP_DTYPE_CREATE('B','BIT',NULL,NULL);

CALSP_DTYPE_CREATE('INTE','INTEGER',NULL,NULL);

CALSP_DTYPE_CREATE('IN0','INT',NULL,NULL);

CALSP_DTYPE_CREATE('BIN','BIGINT',NULL,NULL);

CALSP_DTYPE_CREATE('TIN','TINYINT',NULL,NULL);

CALSP_DTYPE_CREATE('BY0','BYTE',NULL,NULL);

CALSP_DTYPE_CREATE('SIN','SMALLINT',NULL,NULL);

--使用创建的类型别名:

CREATE TABLE TAB_02(C1 B,C2 INTE,C3 IN0,C4 BIN,

C5 TIN,C6 BY0,C7 SIN);

INSERT INTO TAB_02 VALUES(1,2147483647,2147483647,9223372036854775807,

127,127,32767);

INSERT INTO TAB_02 VALUES(0,-2147483648,-2147483648,-9223372036854775808,

-128,-128,-32768);

INSERT INTO TAB_02 VALUES(NULL,NULL,NULL,NULL,NULL,NULL,NULL);

SELECT * FROM TAB_02;

--删除创建的类型别名:

CALSP_DTYPE_DELETE('B');

CALSP_DTYPE_DELETE('INTE');

CALSP_DTYPE_DELETE('IN0');

CALSP_DTYPE_DELETE('BIN');

CALSP_DTYPE_DELETE('TIN');

CALSP_DTYPE_DELETE('BY0');

CALSP_DTYPE_DELETE('SIN');

--清除类型别名运行环境:

CALSP_INIT_DTYPE_SYS(0);

--清除测试环境:

DROP TABLE TAB_02;

3. 其他

3.1 约束

3.1.1 非空约束

3.1.2 唯一约束

3.1.3 检查约束

3.1.4 缺省约束

3.1.5 主键约束

当pk_with_cluster等于1时,需要显示带上not cluster才能创建非聚簇索引主键。

--pk_with_cluster=1,建表的时候不指定主键,改参数的时候用1,不用重启生效。

select sf_get_para_value(2,'PK_WITH_CLUSTER');

create table "SYSDBA"."TABLE_1"("C1" CHAR(10));

alter table "SYSDBA"."TABLE_1" add constraint "P_1" primary key("C1");

calsp_tabledef('SYSDBA','TABLE_1');

--pk_with_cluster=1,尝试删除主键,可以成功:

alter table "SYSDBA"."TABLE_1" drop constraint "P_1" ;

--pk_with_cluster=1,建表的时候指定主键,改参数的时候用1,不用重启生效。

select sf_get_para_value(2,'PK_WITH_CLUSTER');

create table "SYSDBA"."TABLE_1"("C1" CHAR(10),CONSTRAINT "P_1" primary key("C1"));

calsp_tabledef('SYSDBA','TABLE_1');

--pk_with_cluster=1,建表时指定主键,尝试删除主键,不能成功:

alter table "SYSDBA"."TABLE_1" drop constraint "P_1" ;

--pk_with_cluster,聚集主键都不允许删除,都不支持add cluster primary key

alter table "SYSDBA"."TABLE_22" add constraint "P_1" cluster primary key("C1");

--只能add not cluster primary key

alter table "SYSDBA"."TABLE_22" add constraint "P_1" not cluster primary key("C1");

--约束的启用和禁用:

--启用的时候先启用其他约束,再启用外键。

alter table xxx enable constraint 约束名 [<CHECK 选项>]

--禁用的时候先禁用外键,再禁用其他的。

alter table xxx disable constraint 约束名 [RESTRICT | CASCADE]

--禁用主键约束:

create table "SYSDBA"."T3"( "C1" CHAR(10));

alter table "SYSDBA"."T3" add constraint "p_key" primary key("C1");

insert into T3 values(1);

--应该报错

insert into T3 values(1);

alter table T3 DISABLE CONSTRAINT "p_key";

--应该正常

insert into T3 values(1);

--应该报错,删除重复数据之后应该正常

alter table T3 ENABLE CONSTRAINT "p_key";

--应该报错。

insert into T3 values(1);

3.1.6 外键约束

3.2 自增列

插入数据时,报错“仅当指定列列表,且SET IDENTITY_INSERT为ON时,才能对自增列赋值”?

一般情况下,当数据表中,某一列被设置成了标识列之后,是无法向标识列中手动的去插入标识列的显示值。但是,可以通过设置SET IDENTITY_INSERT属性来实现对标识列中显示值的手动插入。

语法:

1).SET IDENTITY_INSERT 表名 ON :表示开启对标识列显示值插入模式,允许对标识列显示值进行手动插入数据。

2).SET IDENTITY_INSERT 表名 OFF:表示关闭对标识列显示值的插入操作,标识列不允许手动插入显示值。

注意:IDENTITY_INSERT的开启ON和关闭OFF是成对出现的,所以,在执行完手动插入操作之后,记得一定要把IDENTITY_INSERT设置为OFF,否则下次的自动插入数据会插入失败。

DM 支持 INT 和 BIGINT 两种数据类型的自增列,并提供以下函数查看表上自增列的当前值、种子和增量等信息:

1.IDENT_CURRENT:获得表上自增列的当前值;

2.IDENT_SEED:获得表上自增列的种子信息;

3.IDENT_INCR:获得表上自增列的增量信息。

--建表,包含自增列:

create table TAB_03(c1 int identity(1,2), c2 char);

insert into TAB_03(c2) values('a');

insert into TAB_03(c2) values('b');

select * from TAB_03;

--获取自增列信息

select ident_current('SYSDBA.TAB_03');

select ident_seed('SYSDBA.TAB_03');

select ident_incr('SYSDBA.TAB_03');

--建表,包含自增列,起始值为负数:

create table TAB_04(c1 bigint identity(-777,2), c2 char);

insert into TAB_04(c2) values('a');

insert into TAB_04(c2) values('b');

select * from TAB_04;

3.3 回滚到保存点

--建表并插入数据:

drop table if exists t1;

create table t1 (c1 int,c2 varchar(10));

insert into t1 values(1,'aa');

--查询表数据:

select * from t1;

--设置保存点:

savepoint A;

--再插入一条数据:

insert into t1 values(2,'bb');

--查询表数据(同一个会话下能查询到未提交的事务结果):

select * from t1;

--回滚到保存点:

ROLLBACK TO SAVEPOINT A;

--再查询表数据:

select * from t1;

3.4 关于读数据

--在事务执行前设置事务的隔离级别为读提交:

SET TRANSACTION ISOLATION LEVEREAD COMMITTED;

3.4.1 脏读

所谓脏读就是对脏数据的读取,而脏数据所指的就是未提交的已修改数据。也就是说,一个事务正在对一条记录做修改,在这个事务完成并提交之前,这条数据是处于待定状态的(可能提交也可能回滚),这时,第二个事务来读取这条没有提交的数据,并据此做进一步的处理,就会产生未提交的数据依赖关系,这种现象被称为脏读。

如果一个事务在提交操作结果之前,另一个事务可以看到该结果,就会发生脏读。

3.4.2 不可重复读

一个事务先后读取同一条记录,但两次读取的数据不同,我们称之为不可重复读。

如果一个事务在读取了一条记录后,另一个事务修改了这条记录并且提交了事务,再次读取记录时如果获取到的是修改后的数据,这就发生了不可重复读情况。

3.4.3 幻像读

一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为幻像读。

3.5 隔离级别

在SQL-92标准中,定义了四种隔离级别:读未提交、读提交、可重复读和串行化。每种隔离级别下对于读数据有不同的要求,如下图:

 

DM数据库支持三种事务隔离级别:读未提交、读提交和串行化。

其中,读提交是DM数据库默认使用的事务隔离级别。

可重复读升级为更严格的串行化隔离级。

3.5.1 事务的隐式提交

1.当连接的属性设置为自动提交时,每执行一条语句都会提交事务;

2.当连接的属性设置为手动提交

而DDL自动提交开关(DDL_AUTO_COMMIT)打开时,遇到任一DDL语句系统会自动提交该DDL语句及之前的DML操作;

而当该开关关闭时,只有CREATE TABLESPACE和ALTER DATABASE两种DDL语句,物化视图刷新语句以及之前的DML和DDL操作会自动提交;

3.事务所在的程序正常结束和用户退出;

4.系统非正常终止时。

call sp_set_para_value(2,'COMPATIBLE_MODE',1);

--重启服务器

--1表示从ini读取,2表示从内存读。

--查询结果都是1

select sf_get_para_value(1,'COMPATIBLE_MODE') 兼容模式;

select sf_get_para_value(2,'COMPATIBLE_MODE') 兼容模式;

--以下查询结果都是1,但由于compatible_mode=1,按照管理员手册,理论上来说,ddl_auto_commit实际生效的是0

select sf_get_para_value(1,'DDL_AUTO_COMMIT') ddl_auto;

select sf_get_para_value(2,'DDL_AUTO_COMMIT') ddl_auto;

--打开两个disql,其中一个创建表,另一个修改表名。

--会话1创建表

create table t66(C1 int);

--会话2修改表名,居然可以修改成功,也就是说前一个disql中创建表的语句提交了。

alter table t66 rename to t666;

--把兼容模式改回来

call sp_set_para_value(2,'COMPATIBLE_MODE',0);

--手动修改DDL_AUTO_COMMIT=0

--重启

--1表示从ini读取,2表示从内存读。

--查询结果都是0

select sf_get_para_value(1,'COMPATIBLE_MODE') 兼容模式;

select sf_get_para_value(2,'COMPATIBLE_MODE') 兼容模式;

--以下查询结果都是0

select sf_get_para_value(1,'DDL_AUTO_COMMIT') ddl_auto;

select sf_get_para_value(2,'DDL_AUTO_COMMIT') ddl_auto;

--打开两个disql,其中一个创建表,另一个删除修改表名。

--会话1创建表

create table t88(C1 int);

--会话2修改表名,一直等待,ddl_wait_time=10,等待时间超过十秒。

alter table t88 rename to t888;

--会话1执行commit;

--会话2马上修改表名成功。

--说明会话1的ddl确实没有手动提交,但是为什么会话2的ddl等待时间超过10s呢

DISQL连接到服务器后第一条SQL语句或者事务结束后的第一条语句就标记着事务的开始。

在手动提交模式下,DM数据库在遇到以下SQL语句时自动提交前面的事务:

1.CREATE;

2.ALTER;

3.TRUNCATE;

4.DROP;

5.GRANT;

6.REVOKE;

7.审计设置语句。

3.5.2 读未提交隔离级

读未提交隔离级别是最不严格的隔离级别。实际上,在使用这个隔离级别时,有可能发生脏读、不可重复读和幻像。一般来说,读未提交隔离级别通常只用于访问只读表和只读视图,以消除可见性判断带来的系统开销,提升查询性能。

用户可以在事务开始时使用以下语句,设定事务为读未提交隔离级:

SET TRANSACTION ISOLATION LEVEREAD UNCOMMITTED;

--在事务执行前设置事务的隔离级别为读未提交:新建一个查询会话窗口3,在会话3中执行:

SET TRANSACTION ISOLATION LEVEREAD UNCOMMITTED;

--建表并插入数据:

create table t2(c1 int,c2 varchar(10));

insert into t2 values(1,'test1');

--新建一个查询会话窗口4,在会话4中执行:

SET TRANSACTION ISOLATION LEVEREAD UNCOMMITTED;

select * from t2;

3.5.3 读提交隔离级(默认)

DM数据库的读提交隔离可以确保只访问到已提交事务修改的数据,保证数据处于一致性状态,能够满足大多数应用的要求,并最大限度的保证系统并发性能,但可能会出现不可重复读取和幻像读。

用户可以在事务开始时使用以下语句设定事务为读提交隔离级:

SET TRANSACTION ISOLATION LEVEREAD COMMITTED;

--在事务执行前设置事务的隔离级别为读提交:

SET TRANSACTION ISOLATION LEVEREAD COMMITTED;

--建表并插入数据:

create table t1 (c1 int,c2 varchar(10));

insert into t1 values(1,'aa');

--新开一个查询会话窗口(记为会话2),查询表数据:

select * from t1;

--在原来的查询会话1中执行提交:

commit;

--在会话2中再次查询表数据:

select * from t1;

3.5.4 串行化隔离级

在要求消除不可重复读取或幻像读的情况下,我们可以设置事务隔离级为串行化。跟读提交隔离级相比,串行化事务的查询本身不会增加任何代价,但修改数据可能引发“串行化事务被打断”错误。

具体来说,当一个串行化事务试图更新或删除数据时,而这些数据在此事务开始后被其他事务修改并提交时,DM数据库将报“串行化事务被打断”错误。

如果系统中存在长时间运行的写事务,并且该长事务所操作的数据还会被其他短事务频繁更新的话,最好避免使用串行化事务。

用户可以在事务开始时使用以下语句设定事务为串行化隔离级:

SET TRANSACTION ISOLATION LEVESERIALIZABLE;

--在事务执行前设置事务的隔离级别为读可串行化:新建一个查询会话窗口5,在会话5中执行:

SET TRANSACTION ISOLATION LEVESERIALIZABLE;

--建表并插入数据:

create table t3(c1 int,c2 varchar(10));

insert into t3 values(1,'test3aaa');

--新建一个查询会话窗口6,在会话6中执行:

--SET TRANSACTION ISOLATION LEVESERIALIZABLE;

SET TRANSACTION ISOLATION LEVESERIALIZABLE;

update t3 set c2='test3bbb' where c1=1;

--会话5中的事务5进行提交:

commit;

--在会话6中执行(应该查不到数据):

select * from t3;

3.5.5 只读事务

只读事务只能访问数据,但不能修改数据。并且只读事务不会改变事务原有的隔离级。

用户可以在事务开始时使用以下语句,设定事务为只读事务:

SET TRANSACTION READ ONLY;

--在sql查询会话1中执行建表并插入数据:

drop table t1;

create table t1 (c1 int,c2 varchar(10));

insert into t1 values(1,'aa');

commit;

--新建一个sql查询会话2,设置事务属性为只读:

SET TRANSACTION READ ONLY;

--会话2中执行查询表数据:select * from t1;

--会话2中执行修改表数据(会报错):

INSERT INTO T1 VALUES(2,'BB');

--会话2中执行:提交之前的事务,并设置新事务属性为读写:

commit;

SET TRANSACTION READ WRITE;

--会话2中执行修改表数据:

INSERT INTO T1 VALUES(2,'BB');

select * from t1;

3.6 死锁检测和自动解锁

--用管理员用户登录命令行工具,这个用户登录记为SESSION1,并设置为非自动提交模式,建表a并上排他锁,再插入一条数据:

create table a(c1 int, c2 varchar(20));

LOCK TABLE a IN EXCLUSIVE MODE;

insert into a values(1,'from session1');

--用管理员用户登录命令行工具,这个用户登录记为SESSION2,并设置为非自动提交模式,建表b并上排他锁,再插入一条数据:

create table b(c1 int, c2 varchar(20));

LOCK TABLE b IN EXCLUSIVE MODE;

insert into b values(2,'from session2');

--在session1中执行更新b表数据的操作:

--由于b表被session2独占尚未提交,因此session1的update操作被阻塞。

update b set c2='from session1' where c1 =2;

--在session2中执行更新表a数据操作:

--死锁,Session1更新表b被阻塞,session2又更新表a被阻塞

update a set c2='from session2' where c1 =1;

--在session2中执行提交操作(此时自动解锁了),再查询表b数据:

commit;

select * from b;

--在session1中执行提交操作,再查询表a数据:

commit;

select * from a;

--在sql查询会话1中执行建表并插入数据:

drop table t1;

create table t1 (c1 int,c2 varchar(10));

insert into t1 values(1,'aa');

commit;

--管理员新建一个用户并将表t1的所有操作权限授予该用户:

drop user u1;

create user u1 identified by "Dameng123";

grant alon t1 to u1;

--用户u1登录数据库,执行查询表t1,可查看:

select * from sysdba.t1;

--管理员在会话1中执行对t1手动上排他锁:

LOCK TABLE t1 IN EXCLUSIVE MODE;

--用户u1/SYSDBA在会话2中再执行查询表t1,都不可查看,处于一直等待状态:

select * from sysdba.t1;

--会话1中commit后自动解锁,会话2出现结果集。

commit;

3.7 嵌套事务

--执行以下sql脚本建表并插入数据:

drop table t1 ;

drop table t2;

CREATE TABLE t1 (c1 INT);

INSERT INTO t1 VALUES(1);

--遇到create,所以隐式提交之前的语句。

CREATE TABLE t2(c1 INT);

INSERT INTO t2 VALUES(2);

--在PL/SQL中定义嵌套的自治事务:

DECLARE

PRAGMA AUTONOMOUS_TRANSACTION;

BEGIN

INSERT INTO t1 VALUES(301);

COMMIT;

-- 1,301

select 1,* from t1;

--空,说明只提交了301

select 2,* from t2;

BEGIN

INSERT INTO t2 VALUES(302);

COMMIT;

--302,说明只提交了302

select 3,* from t2;

END;

END;

/

--(这一步另外打开一个会话查询一下,结果为302,说明t2中插入2的语句未提交)

select 4,* from t2;

--执行回滚,回滚未提交的事务,即t2中插入2的语句:

ROLLBACK;

--1,301

select 5,* from t1;

--302

SELECT 6,* FROM t2;

--以上查询出的结果说明:

--自治事务提交的时候,不会影响主事务的提交。

--已提交的自治事务,不受主事务回滚的影响。

3.8 ACID

3.8.1 原子性(atomicity)

或称不可分割性

事务是数据库的逻辑工作单位,事务中包括的诸操作要么都做,要么都不做。

--原子性:一个事务全部执行:

drop table if exists t1;

create table t1 (c1 int,c2 varchar(10));

insert into t1 values(1,'aa');

insert into t1 values(2,'bb');

insert into t1 values(3,'cc');

update t1 set c2='dd' where c1=2;

delete from t1 where c1=3;

commit;

--aa,dd

select * from t1;

--原子性:一个事务全部回滚:

drop table if exists t2;

create table t2 (c1 int,c2 varchar(10));

insert into t2 values(1,'aa');

insert into t2 values(2,'bb');

rollback;

--空

select * from t2;

3.8.2 一致性(consistency)

事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。

如果数据库系统运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是不一致的状态。

--某测试用例里这样验证,不太理解:

--一致性:SYSDBA执行:

drop table t1;

create table T1(c1 int unique, c2 varchar(10));

insert into T1 values (1, 'a');

insert into T1 values (2, 'b');

drop table t2;

create table T2(c1 int references T1(c1),c2 date);

insert into T2 values (1,'2012-10-21');--成功

insert into T2 values (2,'2012-10-21');--成功

insert into T2 values (3,'2012-10-21');--失败

3.8.3 隔离性(isolation,又称独立性)

一个事务的执行不能被其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。

--隔离性:(默认读提交隔离级别)

--SYSDBA建表:

drop table t2;

CREATE TABLE T2(

C1 integer not null,

C2 varchar(20),

C3 varchar(30)

);

--SYSDBA创建用户并授予t2表操作的所有权限:

CREATE USER USER01 identified by "Dameng123";

CREATE USER USER02 identified by "Dameng123";

grant alon t2 to USER01;

grant alon t2 to USER02;

--用户user01登录新建一个会话,向表t2插入数据,不提交:

INSERT SYSDBA.T2 values(1,'aaaaa','bbbbb');

--用户user02登录新建一个会话,向表t2插入数据,不提交:

INSERT SYSDBA.T2 values(2,'ccccc','ddddd');

--用户user01查询

select * from SYSDBA.T2;--只能查到自己插入的一条数据

--用户user02查询

select * from SYSDBA.T2;--只能查到自己插入的一条数据

--用户user01和user02分别在各自的会话中执行提交:

commit;

--再各自查询t2表,均可看到两条数据

select * from SYSDBA.T2;

3.8.4 持续性(durability)

持续性也称永久性(Permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。同时,事务也是恢复和并发控制的基本单位。

--持久性,执行:

drop table t3;

create table t3 (c1 int,c2 varchar(10));

insert into t3 values(1,'aa');

COMMIT;

ROLLBACK;

--查询表数据,已提交的事务无法回滚:

select * from t3;

3.9 多版本

在多版本控制以前,数据库仅通过锁机制来实现并发控制。数据库对读操作上共享锁,写操作上排他锁,这种锁机制虽然解决了并发问题,但影响了并发性。例如,当对一个事务对表进行查询时,另一个对表更新的事务就必须等待。

DM 数据库的多版本实现完全消除了行锁对系统资源的消耗,查询永远不会被阻塞也不需要上行锁,并通过 TID 锁机制消除了插入、删除、更新操作的行锁。数据库的读操作与写操作不会相互阻塞,并发度大幅度提高。

(xxg:死锁的时候,查询语句一直等待。)

DM 数据库基于物理记录和回滚记录实现行级多版本支持,数据页中只保留物理记录的最新版本,通过回滚记录维护历史版本,所有事务针对特定的版本进行操作。

--管理员执行建表:

CREATE TABLE T1(C1 INT,C2 VARCHAR(20));

--再建一个存储过程向表T1插入数据:

create or replace procedure proc1 as declare i int;

begin

for i in 1 ..1000000 loop

insert into T1 values(i,'test1');

if (i%5000)=0 then

commit;

end if;

end loop;

end;

--管理员新建一个用户U1并授予该用户对表T1的操作权限:

create user u1 identified by "Dameng123";

grant all on t1 to u1;

--用户U1登录数据库

--管理员执行存储过程向T1插入1000000条数据:

call proc1;

--在存储过程执行过程中,用户U1多次执行查询表T1的操作:

select count(*) from SYSDBA.T1;

--用户U1查询成功,说明写操作不阻塞同时执行的读操作。

--由于存储过程是每插入5000行执行一次提交操作,因此每次查询到的数据条数都不一样,逐次增多。

更多资讯请上达梦技术社区了解: https://eco.dameng.com

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值