表是数据库中负责数据存储的对象,在RDBMS中,数据以行、列的形式存储在表中。Oracle中表有很多种类型,最基础且应用最常用的类型就是堆表(Heap-Organized Table),本文列举了Oracle堆表的常用管理操作。
一、表的类型简介
数据可以有不同的组织形式,生命周期等特点,面对不同的数据存储场景需求,Oracle提供了很多类型的表,主要类型有:
- 堆表(Heap-Oragnized Tables)
- 索引组织表(Index-Organized Tables)
- 分区表(Partitioned Tables)
- 外部表(External Tables)
- 聚簇表(Clustered Tables)
根据表的其他特点还可以划分出临时表(Temporary Tables)、不可变表(Immutable Tables),区块链表(Blockchain Tables)等。
而堆表(Heap-Organized Tables)是Oracle种最基础的数据存储对象,之所以叫做堆表是由于其底层存储的数据结构是堆(Heap,一种无序的数据集合)。当使用create table创建表时,默认创建的就是堆表。
二、表的创建方法
表在逻辑上由行(Row)和列(Column)组成,在创建一张表时,需要定义下列内容:
- 表名
- 一个或多个列(列的定义至少包含列名和数据类型,也可以从已有表复制定义)
- 表的其他属性(分区、存储、类型等等),这些是可选项,如果没有特殊指定,Oracle会按默认配置创建表。
2.1 使用create table语句创建表
表由create table语句创建,创建一个最简单的表,只需要指定表名,列名(列的数据类型)即可:
create table mytab (
id int,
name varchar2(32));
上面示例中我们仅指定了最少信息,实际上表还有许多其他属性,可以用dbms_metadata.get_ddl来查询表的DDL语句:
select dbms_metadata.get_ddl('TABLE','MYTAB') from dual;
可以看到实际存储的定义比我们指定的复杂的多,由于很多属性没有显式指定,所以它们都是默认值:
- 表名变成了hr.mytab,这里使用hr用户连接,因此表创建在该用户的默认schema中(create any table权限可以在别人的schema中创建表)。
- int类型显示的是number(*,0),int实际是number的子类型,小数部分为0。
- segment creation deffered,段延迟创建,即仅创建了表定义,实际存储数据的段会在第一条数据插入时才创建(可以节省空间,特别是存在大量小表的环境)。与之对应的选项是segment creation immediate, 创建表定义时,同时分配存储段。
- pctfree 10 pctused 40 是数据块容量使用策略,普通用户不建议调整。
- initrans 1 maxtrans 255 是事务数量管理
- nocompress 表内数据不压缩
- logging 表上操作的记录redo日志
- tablespace “users” 表创建在users表空间
注:schema是Oracle为每个用户分配的一个对象逻辑容器(和用户名相同),当某个用户创建数据库对象时,默认就会创建在自己的schema中。你可以把它当作一个命名空间,不同schema中的同名对象不会冲突,可以使用schema.object_name的命名方式来引用对象。
除了让Oracle自动创建和管理数据段,还可以使用storage子句来自己指定表的物理存储属性(指定表的初始/最大大小):
create table mytab2 (
id int,
name varchar2(32))
storage (initial 12m next 4m maxsize 1G);
- 这里使用storage子句指定表初始大小为12m,每次扩展大小为4m,最大为1G
2.2 复制已有表定义
create table …. as select ….; 语句可以复制一张已存在的表定义,这个语句包含2个部分,建表部分create table (DDL)和查询部分select(DML)。
下面语句从employees表复制一张employees_copy,仅复制表结构,不填充数据:
create table employees_copy as select * from employees where 1=2;
- where 1=2 是一个永远无法成立的条件,所以不会复制数据,去除该条件则所有数据也将填充到新表中。
- select * 代表复制所有的列,也可以显式的指定列名(复制过程中也可以修改列名),仅复制部分表结构。
2.2.1 使用nologging提升复制性能
使用create table … as select …; 语句复制一张表并填充数据时,如果表的数据量非常大,那么复制的操作可能给系统带来比较大的压力。在复制时使用nologging选项可以提升性能,nologging会减少数据复制时生成的redo日志(从源表复制数据,redo日志并不重要,就算操作失败了重新复制就行)。
而且nologging选项仅影响create table时数据复制操作(不影响表本身的logging属性),后续该表上的insert,update语句都会正常生成redo日志,不用担心崩溃时数据会丢失:
create table employees_copy2 as select * from employees nologging;
三、表基础维护
表在创建后经常随着业务需求进行调整,表的可变更选项非常多,这些变更选项可以在create table时指定,也可以在表创建完成后通过alter table修改。
3.1 新增/修改/删除/重命名列
列的定义通常是变更最频繁的,随着业务的需求的变化经常需要对列的属性进行修改。
3.1.1 新增列
alter table …. add [column] … 语句可以在表中新增列,新增时需要指定列的名称,数据类型等其他属性(和建表时相同)。当一个新的列被添加至表中,已有数据该列上的值都是null,你也可以指定default,为已有的数据填充默认值。
alter table mytab add salary number(8,2) default 1000;
alter table mytab add bonus number(8,2);
- 示例为表mytab新增了salary和bonus列,其中salary列指定了默认值属性(默认1000)
- 表中已有数据salary列都被更新为默认值1000,而bonus列全部为null
3.1.2 修改列
修改列是对已有数据列定义的更新,例如数据类型,大小,默认值等,语法和新增列基本相同,只是将关键字add替换为modify。
alter table mytab modify salary number(10,2) not null;
- 示例将salary列的数据类型修改为number(10,2) 删除了default属性,新增了not null约束
3.1.3 删除列
通过 alter table … drop … 可以删除1个或多个列,删除列时,列的定义及数据都会被清除,存储空间会被回收,注意在大表此操作可能会影响性能:
alter table mytab drop column bonus;
3.1.4 重命名列
通过alter table …. rename column … to …语句可以重命名指定列,同时对该列上的依赖对象(函数索引,check约束等)Oracle也会自动进行更新并保持有效。
alter table mytab rename column salary to salary2;
3.2 不可用列
在3.1.3中提到,删除某列时会清除该列数据,如果表的数据量非常大,这个操作可能会占用大量系统资源,造成性能下降。Oracle提供了另一个折中的方案:先将列标记为不可用(逻辑删除),等待系统负载较轻时再清除数据(物理删除)。当列被标记为不可用时,它不会被select查询查出来,对于应用来说该列仿佛真的被删除了(你甚至可以重用列名)。
使用 alter table …. set unused … 将某1列或多列标记为不可用,注意列一旦被标记为不可用之后就无法恢复了,后续只能将其删除:
alter table mytab set unused(salary2);
- 示例将salary2列标记为不可用,select语句不再能查出该列
- 后续又新增了一个同名salary2列,也不会冲突
将列标记为不可用后,通过视图dba/all/user_unused_col_tabs可以查看表上不可用列的数量:
select * from user_unused_col_tabs where table_name='MYTAB';
待系统空闲时,使用 alter table … drop unused columns [checkpoint 300]; 物理清除该列的数据并回收空间(使用dorp column显式删除其他列时,不可用列也会被一并删除)。可选的checkpoint子句可以指定清理多少行执行一次检查点(示例中是300),防止在大表上操作时undo表空间过度扩张:
alter table mytab drop unused columns checkpoint 300;
- 删除不可用列后,通过user_unused_col_tabs可以看到该列已经真正被删除了(清理数据)
3.3 隐形列
Oracle隐形列,可以将列对应用隐藏起来,但可以显式调用。例如应用升级前,可以预先对数据库升级,升级中新增的列设置为隐形,不影响当前应用。隐形列可以通过显式指定列名来查询和更新(select */ desc等命令不会显式)。和不可用列只能删除不同,隐形列你可以随意在隐藏和显示两种状态间切换。
隐形列的使用方法可以参考下面的文章:
Oracle 隐形列(Oracle Invisible Columns)
3.4 虚拟列
Oracle虚拟列是没有实际存储数据的,它的值是基于实体列计算而来,所以你不能更新虚拟列,但可以像正常的列一样查询它(虚拟列的值只有在查询时才会进行计算)。虚拟列只能在普通的堆表上使用,例如索引组织表,外部表,临时表等都是不支持虚拟列的。
虚拟列通过as关键字创建,以包含实体列的表达式定义(虚拟列不能引用其他虚拟列):
alter table mytab add salary number(8,2) default 1000;
alter table mytab add income as (salary*1.1); -- 基于salary列的虚拟列
- 示例中新增了实体列salary,随后基于salary新增了虚拟列income,并将值提升了10%
当实体列被虚拟列引用时是无法直接删除的,必须先删除虚拟列:
alter table mytab drop column salary;
- 删除被虚拟列引用的实体列会报错
3.5 移动表至新表空间
用alter table … move [online] tablespace …可以将表移动至新表空间,新表空间可能放在不同的存储设备上,用于均衡I/O负载。online是可选关键字,如果指定则表上运行的DML语句在移动过程中不会被阻塞。
下面将表移动至新表空间new_tbs:
alter table mytab move online tablespace new_tbs;
- 这里切换至sys用户,新建了new_tbs表空间(hr用户没权限)
- 随后切换至hr用户,将表在线移动至new_tbs表空间
3.6 只读模式
alter table … read only/write 可以将表在只读/读写模式之间切换。当表在只读模式下,会禁止一切DML更新或截断表(truncate table),也不可以重命名或删除列,但是会允许新增/修改列,以及对索引,约束等不影响数据的对象进行操作。
对于一些配置表,若表中数据不经常变动,那么非常适合将其置于只读模式来防止程序对其进行意外修改:
alter table mytab read only;
alter table mytab read write;
四、闪回删除
Oracle在执行drop table命令时,并不会直接删除表,而是将其重命名并放入回收站(Recyclebin),如果用户发现误删,可以用闪回删除功能将表恢复(相关索引、约束、触发器等也会一并恢复)。注意只有在drop table命令才可以使用闪回删除,其他例如truncate table, drop user, drop tablespace这些操作都会直接清除表或数据。
闪回删除的具体操作方法可以参考下面文章:
Oracle闪回技术详解及应用
五、闪回表
闪回表的原理是利用undo日志,将整张表的状态(数据)回退到过去某个时间点/SCN/restore_point。该时间点之后的所有变更都会被撤销,而数据库其他的部分不受影响。闪回删除针对的是表这个对象误删除的闪回,而闪回表针对的是表内数据的闪回。
闪回表的具体操作方法可以参考下面文章:
Oracle闪回技术详解及应用
六、常用视图
Oracle提供了一系列数据字典视图,方便查询表的相关信息。视图以dba, all, user开头,分别代表全部/可查看/归属本用户的表信息,只是数据范围不同,部分常用视图列举如下:
- dba/all/user_tables: 查询表的基本信息
- dba/all/user_tab_columns: 查询列的基本信息
- dba/all/user_tab_comments: 查询表和视图的备注信息
- dba/all/user_col_comments: 查询表和视图的列的备注信息
- dba/all/user_tab_statistics: 查询表的统计信息(优化器使用)
- dba/all/user_unused_col_tabs: 查询表中被标记不可用的列
相关阅读:
Oracle分区表详解(Oracle Partitioned Tables)