表
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) - 未找到父项关键字