Oracle数据库基础之6_外部表临时表分区表_物化视图_约束的介绍

表

DUAL是一个公共表,可用它做些计算或返回函数处理结果

外部表:

使用ORGANIZATION external来创建一个外部表,它是一个只读表,其元数据存储在数据库中,但其数据存储在数据库之外。

sql 复制代码
[oracle@ocp ~]$ cat /home/oracle/test.txt
1,10
2,20

SQL> create directory testdir as '/home/oracle';
SQL> grant read,write on directory testdir to hr;
SQL> conn hr/123456
SQL> create table test_exter(hid1 number,hid2 number) ORGANIZATION external 
    (
     type oracle_loader 
     DEFAULT DIRECTORY testdir 
     ACCESS PARAMETERS
    (
      RECORDS DELIMITED BY NEWLINE 
      fields terminated by ','
    )
    location('test.txt')
    );
SQL> select * from test_exter;

      HID1       HID2
---------- ----------
         1         10
         2         20

--ORGANIZATION external --外部表的关键字

--type oracle_loader --外部表有两种类型,这里使用oracle_loader,另一种类型是oracle_datapump

--DEFAULT DIRECTORY testdir --外部表使用文件存放的directory目录

--ACCESS PARAMETERS --参数列表,RECORDS DELIMITED BY NEWLINE表示行由换行符来进行分隔,fields terminated by ','表示字段由逗号来进行分隔

--location --外部表使用的文件名称

临时表:

事务级临时表的数据只在当前事务有效,通过语句:ON COMMIT DELETE ROWS 指定(默认方式,如下两行结果一致)。

CREATE GLOBAL TEMPORARY TABLE TEMP_TAB1(HID NUMBER);

CREATE GLOBAL TEMPORARY TABLE TEMP_TAB1(HID NUMBER) ON COMMIT DELETE ROWS;

sql 复制代码
SQL> CREATE GLOBAL TEMPORARY TABLE TEMP_TAB1(HID NUMBER);
Table created.

SQL> insert into TEMP_TAB1 values (1);
1 row created.

SQL> select * from TEMP_TAB1;
       HID
----------
         1

SQL> commit;
Commit complete.

SQL> select * from TEMP_TAB1;
no rows selected

会话级临时表的数据只在当前会话有效,通过语句:ON COMMIT PRESERVE ROWS指定。

CREATE GLOBAL TEMPORARY TABLE TEMP_TAB2(HID NUMBER) ON COMMIT PRESERVE ROWS;

会话1

sql 复制代码
SQL> CREATE GLOBAL TEMPORARY TABLE TEMP_TAB2(HID NUMBER) ON COMMIT PRESERVE ROWS;
Table created.

SQL> insert into TEMP_TAB2 values (1);
1 row created.


SQL> select * from TEMP_TAB2;
       HID
----------
         1

SQL>  commit;
Commit complete.

SQL> select * from TEMP_TAB2;
       HID
----------
         1

在开一个会话2

sql 复制代码
SQL> insert into TEMP_TAB2 values (2);
1 row created.

SQL> commit;
Commit complete.

SQL> select * from TEMP_TAB2;

       HID
----------
         2

回到会话1

sql 复制代码
SQL> select * from TEMP_TAB2;
       HID
----------
         1

以上说明:会话1、会话2互不影响

进一步得出结论:临时表不存在并发,就不存在锁,每个人看到的都是自己的东西 。

sql 复制代码
SQL> create view vTEMP_TAB2 as select * from TEMP_TAB2;--可以对临时表创建视图
View created.

SQL> create index idx_vTEMP_TAB2 on TEMP_TAB2 (hid);--临时表在用的时候,无法创建索引
create index idx_vTEMP_TAB2 on TEMP_TAB2 (hid)
                               *
ERROR at line 1:
ORA-14452: attempt to create, alter or drop an index on temporary table already
in use

SQL> exit

sqlplus hr/oracle
SQL> create index idx_vTEMP_TAB2 on TEMP_TAB2 (hid);--临时表不用的时候,可以创建索引
Index created.

分区表:

分区表产生的理由:随着表的不断增大,对表的查询会越来越慢。对于数据库中的超大型表,可以通过把它的数据分成若干个片段,每一个片段我们称为一个单个的分区。对于分区的访问,我们不需要使用特殊的SQL查询语句或特定的DML语句,直接单独的操作单个分区,而不是整张表。表进行分区后,逻辑上表仍然是一张完整的表。

例如将不同年份的销售数据,放在不同的表空间,比如01年的销售数据存放到01分区,02年的销售数据存放到02分区,依次类推。对于外部应用程序来说,虽然存在不同的分区,且数据位于不同的表空间,但逻辑上仍然是一张表。

比喻:分区表就像是一栋楼,分区就像是楼层。如果你能知道自己的数据在几层,你就能很快找到。反之如果你没有分区就得按照索引或者全表扫描去找,必然要慢,数据量越大就越慢。

分区的优点:

1、每个分区都是一个独立的SEGMENT段,由于将数据分散到各个分区中,不同分区的数据可以放置到不同的表空间,不同的表空间又可以映射到不同的物理磁盘上,减少了IO争用也减少了数据损坏的可能性

2、可以对单独的分区进行操作,提高性能、可管理性、可用性

3、要查询的数据只在某个分区内时,执行计划可能只去扫描这个分区,而不用扫描全表的所有分区。

分区类型

1、范围分区(range)

2、哈希分区(hash)

3、列表分区(list)

4、关联分区(Reference)

分区表,每个分区就是一个单独的段

sql 复制代码
CREATE TABLE tab_part1(
    hid number,
    hname varchar2(20)
)
PARTITION BY RANGE(hid)
( 
PARTITION p01 VALUES LESS THAN(100) TABLESPACE users,
PARTITION p02 VALUES LESS THAN(200) TABLESPACE users,
PARTITION p03 VALUES LESS THAN(300) TABLESPACE users,
PARTITION p04 VALUES LESS THAN (MAXVALUE) TABLESPACE users
);
--以上users表空间可以换成不同的表空间名称,比分区p01对应users01表空间,p02对应users02。。。

insert into tab_part1 values(1,'1');
insert into tab_part1 values(101,'101');
insert into tab_part1 values(201,'201');
insert into tab_part1 values(301,'301');



CREATE TABLE tab_nopart1(
    hid number,
    hname varchar2(20)
);

insert into tab_nopart1 values(1,'1');
insert into tab_nopart1 values(101,'101');
insert into tab_nopart1 values(201,'201');
insert into tab_nopart1 values(301,'301');

select * from user_tab_partitions where table_name='TAB_PART1'
select * from dba_segments where segment_name in ('TAB_PART1','TAB_NOPART1')
--TAB_PART1有四行,说明有4个段
--TAB_NOPART1只有一行,说明只有1个段


select * from tab_part1 partition (p01);--只查询某个分区的值

select * from tab_part1 partition (p01)
union all
select * from tab_part1 partition (p02);
--查询多个分区


SQL> set autotrace traceonly
SQL> select * from tab_part1 where hid>201;
------------------------------------------------------------------------------------------------------
| Id  | Operation                | Name      | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT         |           |     1 |    25 |    27   (0)| 00:00:01 |       |       |
|   1 |  PARTITION RANGE ITERATOR|           |     1 |    25 |    27   (0)| 00:00:01 |     3 |     4 |
|*  2 |   TABLE ACCESS FULL      | TAB_PART1 |     1 |    25 |    27   (0)| 00:00:01 |     3 |     4 |
------------------------------------------------------------------------------------------------------
--执行计划只去3-4分区内找数据,不用扫描全表的所有分区

分区表的索引:分为local和global两类

local:索引分区和表分区形式一样

global:索引分区和表分区无关

sql 复制代码
create index tt_01 on tab_part1(hid) local;

select * from dba_segments where segment_name='TT_01'
select * from user_ind_partitions where index_name='TT_01'
select * from user_tab_partitions where table_name='TAB_PART1'
--local索引分区和表分区的形式一样


drop index tt_01


create index tt_01 on tab_part1(hid) global
       partition by range(hid)(
       partition i_p1 values less than (200) tablespace example,
       partition i_p2 values less than (maxvalue) tablespace users);

select * from dba_segments where segment_name='TT_01'
select * from user_ind_partitions where index_name='TT_01'
select * from user_tab_partitions where table_name='TAB_PART1'
--Global索引分区和表分区的形式无关

Interval自动分区

Interval支持的Range分区键类型只有number、date、timestamp三种类型

CREATE TABLE tab_part2 (hid number,hname varchar2(20))

PARTITION BY RANGE(hid) INTERVAL (100)

( PARTITION p01 VALUES LESS THAN (100));

number类型:建立的分区表说明100之前的数据放入P01分区中,之后的数据每100放入一个新一个分区,比如102放入一个分区p02,203放入一个分区p03

sql 复制代码
insert into tab_part2 values(1,'1');
insert into tab_part2 values(101,'101');
insert into tab_part2 values(201,'201');
select * from user_tab_partitions where table_name='TAB_PART2';--p01后面的分区名字,系统自动建立


CREATE TABLE tab_part3 (hdate date,hname varchar2(20))
PARTITION BY RANGE(hdate) INTERVAL (NUMTODSINTERVAL(1, 'DAY'))
( PARTITION p01 VALUES LESS THAN (to_date('2018-01-01','yyyy-mm-dd')));
DATE类型:建立的分区表说明2018-01-01之前的数据放入P01分区中,之后的数据每天一个分区

insert into tab_part3 values(sysdate-365,'2017');
insert into tab_part3 values(sysdate,'2018');
insert into tab_part3 values(sysdate+1,'2018');
select * from user_tab_partitions where table_name='TAB_PART3';--p01后面的分区名字,系统自动建立

当如如果建表的时候没有指定interval

修改每100为单位分区则可以手工修改如下

alter table tablename set INTERVAL (100);

--alter table tab_part1 set INTERVAL (100);--报错ORA-14759,因为存在LESS THAN (MAXVALUE)这个分区,需要先把这个分区删除

--alter table tab_part1 drop partition p04;

--alter table tab_part1 set INTERVAL (100);

--之前已经创建的分区P01,P02,P03还存在,后面新增的分区名以SYS_名称开头。如果drop的分区p04已经有数据了,那么就不能直接去drop这个分区了,因为这个分区P04一旦drop掉,这个已经存在分区P04中的数据也被drop掉了

修改每天分区则可以手工修改如下

alter table tablename set INTERVAL (NUMTODSINTERVAL(1, 'DAY'));

修改为每周分区则如下

alter table tablename set INTERVAL(numtodsinterval(7,'day'));

修改为每月分区则如下

alter table tablename set INTERVAL(numtoyminterval(1,'MONTH'));

修改为每年分区则如下

alter table tablename set INTERVAL(numtoyminterval(1,'YERA'));

物化视图

物化视图本质上是一种预先计算并存储查询结果的数据库对象,它允许你将查询结果存储在一个物理位置,以便快速访问,而不是每次需要数据时都重新执行复杂的查询。这是一种以"空间换时间"的策略,即在存储空间上做出一定的牺牲来换取查询性能的显著提升。适合于复杂查询加速和数据仓库预计算的场景。

曾经遇到的案例:一个系统视图dba_network_acl_privileges非常慢,该系统视图很复杂,直接select * from dba_network_acl_privileges显示的执行计划就非常复杂调用了大量的系统对象。我们使用物化视图代替该系统视图后性能提升明显

create materialized view dba_acl_new refresh complete on demand as select * from dba_network_acl_privileges

高频小更新:使用FAST刷新+物化视图日志。

低频大变更:采用COMPLETE刷新减少日志开销。

Rowid 模式

sql 复制代码
1. 创建基表(无主键)
CREATE TABLE products (
    product_code VARCHAR2(50), 
    product_name VARCHAR2(100),
    price NUMBER
);

CREATE TABLE sales (
    sale_id NUMBER,
    product_code VARCHAR2(50),
    sale_date DATE,
    quantity NUMBER
);

-- 插入测试数据
INSERT INTO products VALUES ('P001', 'Laptop', 1000);
INSERT INTO products VALUES ('P002', 'Phone', 500);
INSERT INTO sales VALUES (1, 'P001', SYSDATE, 2);
INSERT INTO sales VALUES (2, 'P002', SYSDATE, 3);
COMMIT;

2、创建物化视图日志(WITH ROWID)
由于基表无主键,物化视图日志必须显式包含 ROWID:

CREATE MATERIALIZED VIEW LOG ON products
WITH ROWID 
INCLUDING NEW VALUES;

CREATE MATERIALIZED VIEW LOG ON sales
WITH ROWID 
INCLUDING NEW VALUES;

3、创建物化视图(FAST刷新)
3.1
CREATE MATERIALIZED VIEW sales_summary_mv
REFRESH FAST ON DEMAND  -- 手动刷新模式(后续通过定时任务触发)
AS
SELECT 
    p.product_code,
    p.product_name,
    p.price * s.quantity AS total_amount,
    p.rowid AS prod_rowid,  -- 必须包含基表的 ROWID
    s.rowid AS sale_rowid   -- 必须包含基表的 ROWID
FROM products p
JOIN sales s 
ON p.product_code = s.product_code;
3.2
CREATE OR REPLACE PROCEDURE refresh_sales_mv AS
BEGIN
    DBMS_MVIEW.REFRESH('sales_summary_mv', 'F');  -- 'F' 表示 FAST 刷新
END;
3.3
BEGIN
    DBMS_SCHEDULER.CREATE_JOB (
        job_name        => 'JOB_REFRESH_SALES_MV',
        job_type        => 'STORED_PROCEDURE',
        job_action      => 'refresh_sales_mv',
        start_date      => SYSTIMESTAMP,
        repeat_interval => 'FREQ=MINUTELY; INTERVAL=180',  -- 每3小时执行一次
        enabled         => TRUE
    );
END;
--From语句列表中的所有基表没有主键的话,那么这些基表的rowid必须出现在select中
--3.1中REFRESH FAST ON DEMAND改成REFRESH FAST ON COMMIT的话,就不需要3.2和3.3了,因为REFRESH FAST ON COMMIT的话,基表数据一提交,物化视图就刷新,不过这样对性能影响比较大,一般不推荐
--3.1中REFRESH FAST ON DEMAND改成REFRESH FAST START WITH SYSDATE NEXT SYSDATE + 3/24的话,就不需要3.2和3.3了,因为会自动建立job没隔3小时定时刷新



基表只有一张表时,使用REFRESH WITH ROWID就可以了
CREATE TABLE table1 (
    sale_id NUMBER,
    product_code VARCHAR2(50),
    sale_date DATE,
    quantity NUMBER
);

INSERT INTO table1 VALUES (1, 'P001', SYSDATE, 2);
INSERT INTO table1 VALUES (2, 'P002', SYSDATE, 3);
commit


CREATE MATERIALIZED VIEW LOG ON table1
WITH ROWID 
INCLUDING NEW VALUES;

CREATE MATERIALIZED VIEW table2_mv
REFRESH WITH ROWID FAST START WITH SYSDATE NEXT SYSDATE + 1/4096
AS
SELECT * from table2

外键约束

父表不能随便删除数据

子表不能随便新增数据

外键约束之父表不能随便删除数据的例子

sql 复制代码
create table test1 (hid number primary key,hname varchar2(10));
create table test2 (hid1 number(10) constraint hid_pk REFERENCES test1(hid),hname1 varchar2(10));
insert into test1 values(1,'1');
insert into test2 values(1,'100');
delete from test1;--报错

解决方法1(先删除子表关联的记录,再删除父表的记录)

sql 复制代码
delete from test2;
delete from test1;

解决方法2(子表的外键约束加上ON DELETE CASCADE,父记录删除的时候子记录被级联删除)

sql 复制代码
insert into test1 values(1,'1');
insert into test2 values(1,'100');
alter table test2 drop constraint hid_pk;
ALTER TABLE test2 ADD CONSTRAINT hid_pk FOREIGN KEY (hid1)  REFERENCES test1 (hid) ON DELETE CASCADE;
delete from test1;

解决方法3(保留子表记录,但是子表对应字段值变成null,如下test2的hid1为null了)

sql 复制代码
insert into test1 values(1,'1');
insert into test2 values(1,'100');
alter table test2 drop constraint hid_pk;
ALTER TABLE test2 ADD CONSTRAINT hid_pk FOREIGN KEY (hid1)  REFERENCES test1 (hid) ON DELETE SET NULL;
delete from test1;

外键约束之子表不能随便新增数据的例子

sql 复制代码
create table test1 (hid number primary key,hname varchar2(10));
create table test2 (hid1 number(10) constraint hid_pk REFERENCES test1(hid),hname1 varchar2(10));
insert into test1 values(1,'1');
insert into test2 values(3,'100');
ORA-02291: 违反完整约束条件 (test2.hid_pk) - 未找到父项关键字
相关推荐
智商偏低1 小时前
【无标题】
服务器·数据库·oracle
雨落在了我的手上2 小时前
MySQL数据库基础(1):MySQL的安装-Windows
数据库·mysql
涉密IT资质笔记2 小时前
涉密人员脱密期管理规范:期限分级模型、就业限制边界与违规认定标准
java·服务器·前端·网络·数据库
honsor2 小时前
PoE温湿度传感器:一根网线供电+通信,即插即用,机房/配电室/仓库温湿度监测首选
运维·服务器·网络·数据库·人工智能·安全
XM_jhxx2 小时前
简会AI图纸识别系统功能上新:模板、公差、量具个性化配置上线!
数据库·人工智能·软件工程
java资料站2 小时前
十、向量数据库
数据库
Ivanqhz3 小时前
干涉图着色
java·服务器·网络·数据库·人工智能·深度学习
꯭自꯭闭꯭3 小时前
达梦DMDSC主备搭建
linux·服务器·数据库
用户EasyAdminBlazor4 小时前
EasyAdminBlazor 审批并发控制:两个人同时审批为什么只能成功一个?
数据库