数据库(database,简称DB):用于存放数据的仓库。
数据库管理系统(Database Management System,DBMS):指数据库系统中对数据进行管理的软件系统。
数据库管理员(database administrator,DBA):是负责对数据进行规划、设计、协调、维护和管理的人员。
Oracle Database,简称Oracle。是美国ORACLE(甲骨文)公司的一款对象关系型的数据库管理系统(ORDBMS)。目前在数据库市场上占有主要份额。
Oracle数据库:相关的操作系统文件(即存储在计算机硬盘上的文件)集合,这些文件组织在一起,成为一个逻辑整体,即为Oracle数据库。
Oracle数据库作用:数据库用来存储数据的集合,Oracle用它来存储和管理相关的信息,注意数据库必须要与内存里的实例合作,才能对外提供数据管理服务。
Oracle实例:位于物理内存里的数据结构,它由操作系统的多个后台进程和一个共享的内存池所组成,共享的内存池可以被所有进程访问。
Oracle实例 = 进程 + 进程所使用的内存[SGA(System Global Area)]
注意:
命令 | 作用 | 操作示例 |
---|---|---|
Connect | 切换连接用户,简写conn | conn 用户名/密码 |
Show user | 显示当前登录用户 | show user |
Host |
执行操作系统命令 | host mkdir d:/testoracle:创建文件夹 |
Spool | 导出记录到文本 | 1.spool d:\testoracle\test.txt:确认文件导入位置 2.spool off:确认导入到文件中 |
Clear screen | 清屏 | clear screen |
Start d:\test.sql | 执行文件系统中的SQL语句(注:start命令等同于@,即:@d:\test.sq;) | start d:\test.sql |
Desc | 显示表结构 | desc 表名 |
Show error | 显示错误信息 | show error |
exit | 退出 | exit |
注意:sys和system在登录Oracle工具时,sys只能以系统管理员(sysdba)或系统操作员(sysoper)的权限登录,而system可以直接登录(normal)
SQLPLUS中
{<username>[/<password>][@<connect_identifier>]/}[as {sysdba|sysoper}]
# username/password:指定数据库账户用户名和密码
# connect_identifier:数据库连接的连接标识符(服务器名)。如果没有连接标识符,SQL PLUS将连接到默认数据库
# sysdba、sysoper 选项是数据库管理权限
# sysdba:数据库管理员的权限
# sysoper:数据库操作员的权限
sysdba是管理员Oracle实例,它的存在不依赖于整个数据库完全启动了,它已经存在,以sysoper身份登录,装载数据库、打开数据库,只有数据库打开了或者说数据库完全启动后,dba角色才有了存在的基础!
SQL> conn sys/密码 [@orcl] as sysdba
已连接。
--注意: 密码安装时未设置,随便输入密码都可以,但一定要身份登录
SQL> conn /as sysdba
已连接。
--注意:也可以登入,默认是sys身份
SQL> conn system/密码 [@orcl] [as sysdba]
已连接。
--注意: 当system使用sysdba登入时,实际上是sys登录,可以使用命令show user查看到!
SQL> sqlplus /nolog
当用户连接数据库出现一下情况时,怎么办?
SQL> conn scott/tiger;
ERROR:
ORA-28000: the account is
警告: 您不再连接到 ORACLE
解决方案如下:
--语法规则
alter user username account unlock;
--操作实例:注意前提条件,使用sys或者system身份登录,才能行使权限
alter user scott account unlock;
--使用scoot用户连接数据库
conn scott/tiger;
--查看表名
select table_name from user_tables;
数据库与表空间
表空间与数据文件
--语法格式:
create [temporary] TABLESPACE tablespace_name TEMPFILE|DATAFILE 'XX.dbf' SIZE XX;
--操作示例:
--创建一张永久表空间,位置默认
create tablespace test1_tablespace datafile 'test1file.dbf' size 10m;
--查看永久表存在的位置,如果报未选定行错误,则表空间名需要大写
select file_name form dba_data_files where tablespace_name = 'TEST1_TABLESPACE';
--创建一张临时表空间,位置默认
create temporary tablespace temptest1_tablespace tempfile 'tempfile1.dbf' size 10m;
--查看临时表存在的位置,如果报未选定行错误,则表空间名需要大写
select file_name from dba_temp_files where tablespace_name = 'TEMPTEST1_TABLESPACE';
--语法格式:
create user identified by default tablesapce temporary tablespace ;
--操作示例:
create user qzp identified by qzpwjw6015 default tablespace test1_tablespace temporary tablespace temptest1_tablespace;
--语法格式:
select username from dba_users;
--注意:此命令是查看所有用户
--语法格式:
grant 权限 to 用户名;
--操作示例:
--注意:前提条件是sys或者system用户登录
grant connect to qzp;
授权成功-- 成功授权了可以连接数据库的权限
--连接用户
connect username/password;
--更改密码
alter user username identified by newpassword;
--锁定用户而不删除其用户
alter user username account lock;
--删除用户
drop user username;--这个用户下没有任何对象才可以使用,否则报错
drop user username cascade;--删除这个用户以及这个用户下的所有对象
# 注意:加上cascade则将用户连同其创建的东西全部删除
注意:对于普通用户,授予connect,resource权限;对于DBA管理用户,授予dba权限。
--语法格式:
create role rolename;
--操作示例:
create role manager;
--语法格式:
grant 权限 TO rolename;
--操作示例:
grant create table,create view to manager;
--语法格式:
grant 角色 to 用户;
--操作示例
grant manager to user01,user02;
--语法格式:
revoke 角色 from 用户;
--操作示例
revoke manager from user01,user02;
--语法格式:
drop role rolename;
--操作示例
drop role rolename;
--查看Oracle所有系统权限
select * from SYSTEM_PRIVILEGE_MAP;
--常用的系统权限如下:
CREATE SESSION --创建会话
CREATE SEQUENCE --创建序列
CREATE TABLE --创建会话
CREATE USER --创建用户
ALTER USER --更改用户
DROP USER --删除用户
CREATE VIEW --创建视图
--授权系统权限的语法格式:
grant privilege1[,privilege2,...] to user[,user|role,public] [with grant option];
--public :所有用户都有的角色
--with grant option:使用户同样具有其它分配权限的权利,可将此权限授予给别人
注意:
1).如果使用with grant option为某个用户授予系统权限,那么对于被这个用户授予相同权限的所有用户来说,取消该用户的系统权限并不会级联取消这些用户的相同去权限。
2).系统权限无级联,即A授权予B权限,B授权予C权限,如果A收回B的权限,C的权限不会受影响;系统权限可以跨用户回收,即A可以直接收回C用户的权限。
--查看Oracle所有对象权限
select * from table_privilege_map;
--常用的对象权限如下:
select --查询
update --更新
insert --插入
delete --删除
all --所有对象权限
--授予对象权限的语法格式:
grant object_priv|all [(columns)] on object to {username|rolename,public} [with grant option];
--object_priv:对象权限
--public :指授予给所有的用户
--with grant option :允许用户再次给其它用户授权
--操作示例:
grant select,update,insert on scott.emp to manager;--授权给角色/用户可以对scott.emp表进行查、改、插入
grant manager to user03;--将角色授权给用户
grant all on scott.emp to user04;
grant update(sal,mgr) on scott.emp to test;
--回收对象权限的语法格式:
revoke {object_privilege1 [,object_privilege2,....]|ALL} ON object from {username|rolename|public}
--操作示例:
revoke all on scott.emp form user04;
相关的数据字典:
--查看系统级别的表空间名字
select tablespace_name from dba_tablespaces;
--查询结果:
TABLESPACE_NAME
----------------------------
SYSTEM --系统的表、视图等存放处,称为系统表空间
SYSAUX --是example的辅助表空间,称为索引表空间
UNDOTBS1 --用户放入撤销信息的表空间,称为回滚表空间
TEMP --临时表空间
USERS --用于存储数据库用户创建的数据库对象,称为用户表空间
EXAMPLE --存放oracle11g的实例的表空间
TEST1_TABLESPACE --自己创建的表空间
TEMPTEST1_TABLESAPCE --自己创建的表空间
--查看普通用户的表空间名字
select tablespace_name from user_tablespaces;
--查询结果:
TABLESPACE_NAME
----------------------------
SYSTEM --系统的表、视图等存放处,称为系统表空间
SYSAUX --是example的辅助表空间,称为索引表空间
UNDOTBS1 --用户放入撤销信息的表空间,称为回滚表空间
TEMP --临时表空间
USERS --用于存储数据库用户创建的数据库对象,称为用户表空间
EXAMPLE --存放oracle11g的实例的表空间
TEST1_TABLESPACE --自己创建的表空间
TEMPTEST1_TABLESAPCE --自己创建的表空间
注意:普通用户无法查看系统级别的表空间
相关的数据字典:
--查看system用户默认表空间和临时表空间信息
select default_tablespace,temporary_tablespace from dba_users where username = 'SYSTEM';
--查看所有系统用户默认表空间和临时表空间信息
select username,default_tablespace,temporary_tablespace from dba_users;
--语法格式:
alter user username default|temporary tablespace tablespace_name;
--操作示例:
alter user user01 default tablespace test1_tablespace temporary tablespace temptest1_tablespace;
特别说明:如果一个表空间设置成脱机状态,表示该表空间暂时不让访问,设置成脱机状态不是删除,当我们需要使用该表空间时还可以将其设置成联机状态,正常使用。
--语法格式:
alter tablespace tablespace_name online|offline;
--操作示例:
--修改表空间 test1_tablespace 为脱机状态
alter tablespace test1_tablespace offline;
--查看修改只有的 test1_tablespace 的状态
select status from dba_tablespaces where tablespace_name = 'TEST1_TABLESPACE';
特别说明:默认是可读可写状态
--语法格式:
alter tablespace tablespace_name read only|read write;
--操作示例:
--修改为只读
alter tablespace test1_tablespace read only;
--查看修改只有的 test1_tablespace 的状态,状态是read only
select status from dba_tablespaces where tablespace_name = 'TEST1_TABLESPACE';
--修改为可读可写
alter tablespace test1_tablespace read write;
--查看修改只有的 test1_tablespace 的状态,状态是online
select status from dba_tablespaces where tablespace_name = 'TEST1_TABLESPACE';
说明:向创建好的表空间里增加数据文件
--语法格式:
alter tablespace tablespace_name add datafile 'filename,dbf' size xx;
--操作示例:
--添加一个test2_datafile.dbf的文件到test1_tablespace表空间中
alter tablespace test1_tablespace add datafile 'test2_datafile.dbf' size 10m;
--查看test1_tablespace表空间中的数据文件
select file_name from dba_tablespaces where tablespace_name = 'TEST1_TABLESPACE';
说明:不能删除表空间中的第一个创建的数据文件,如果需要删除的话,我们需要把整个的表空间删掉。
--语法格式:
alter tablespace tablespace_name drop datafile 'filename,dbf';
--操作示例:
--添加一个test2_datafile.dbf的文件到test1_tablespace表空间中
alter tablespace test1_tablespace drop datafile 'test2_datafile.dbf';
--查看test1_tablespace表空间中的数据文件
select file_name from dba_tablespaces where tablespace_name = 'TEST1_TABLESPACE';
--语法格式:
drop tablespace tablespace_name [including contents];
--操作示例:
--删除test1_tablespace表空间
drop tablespace test1_tablespace;
说明:
若果删除掉表空间,而不删除表空间中的数据文件,则执行如下命令:
drop tablespace tablespace_name;
若果删除掉表空间及其表空间中的数据文件,则执行如下命令:
drop tablespace tablespace_name including contents;
操作 | 解释 |
---|---|
create table | 创建数据库表 |
create index | 创建数据库表的索引 |
drop table | 删除数据库表 |
drop index | 删除数据库表的索引 |
truncate | 删除表的所有行 |
alter table | 更改表结构,增删改 |
alter table add consraint | 在已有的表上增加约束 |
操作 | 解释 |
---|---|
select | 查询数据 |
insert | 添加数据到数据库中 |
delete | 删除数据库中表数据 |
update | 修改数据库中的数据 |
操作 | 解释 |
---|---|
grant | 将权限或角色授予用户或其他角色(授予访问权限) |
revoke | 从用户或数据库角色回收权限(撤销访问权限) |
lock | 对数据库的特定部分进行锁定 |
操作 | 解释 |
---|---|
commit | 提交事务处理 |
rollback | 事务处理回退 |
savepoint | 设置保存点 |
类型 | 含义 |
---|---|
CHAR(length) | 存储固定长度的字符串。参数length指定了长度,如果存储的字符串长度小于length,用空格填充。默认长度是1,最长不超过2000字节。 |
VARCHAR2(length) | 存储可变长度的字符串。length指定了该字符串的最大长度。默认长度是1,最长不超过4000字符。 |
NUMBER(p,s) | 既可以存储浮点数,也可以存储整数等数值类型,p表示数字的最大位数(如果是小数包括整数部分和小数部分,p默认是38位),s是指小数位数。 |
CLOB (Character LOB) |
能储存大量字符 |
BLOB (Binary LOB) |
可存储较大的二进制对象,如图形、视频剪辑和声音 |
DATE | 存储日期和时间,存储纪元、4位年、月、日、时、分、秒,存储时间从公元前4712年1月1日到公元后4712年12月31日 |
操作示例:
number(5,0) --最多可以存储五位整数
number(5,2) --最多可以存储五位数,但最大可以存储999.99的浮点数
语法规则:
CREATE TABLE 表名(
columm_name datatype,...
);
操作示例:
-- 创建一个学生表(student_1),表中包括学号(sno)、姓名(sname)、性别(ssex)、年龄(sage)、出生日期(sbirthday)
CREATE TABLE student_1(
sno VARCHAR2(10),
sname VARCHAR2(30),
ssex VARCHAR2(2),
sage NUMBER(3),
sbirthday DATE
);
完整约束基础语法:
[constraint constraint_name(约束名)] <约束类型>
说明:约束不指定名称时,系统会给定一个名称
操作示例:
方式一:创建表时添加主键
CREATE TABLE student_1(
sno VARCHAR2(10),
sname VARCHAR2(30),
ssex VARCHAR2(2),
sage NUMBER(3),
sbirthday DATE,
constraint sno_pk primary key(sno)
);
方式二:修改表添加主键约束
alter table student_1 add constraint sno_pk primary key(sno);
列级约束
扩展知识:列级约束与表级约束
column [constraint constraint_name] constraint_type
说明:列级约束必须跟在列的定义后面
column,
[constraint constraint_name] constraint_type
说明:表级约束不与列在一起,而是单独定义的
操作示例:
方式一:创建表时添加约束
--创建学生表
CREATE TABLE "student"(
"sid" NUMBER(8,0),
"sname" VARCHAR2(20) NOT NULL,
"ssex" CHAR(2) constraint st_sex not null,
"birthday" DATE,
CONSTRAINT S_ID_PK PRIMARY KEY(S_ID)
);
方式二:修改表添加约束
--语法格式:
alter table table_name add [constraint constraint_name] constraint_type (column);
--操作示例:
alter table table_name modify "sname" not null;
删除约束的方式
--语法规则
ALTER TABLE 表名 DISABLE|ENABLE CONSTRAINT 约束名;
--示例:
ALTER TABLE COURSE DISABLE CONSTRAINT C_ID_PK;
ALTER TABLE COURSE ENABLE CONSTRAINT C_ID_PK;
-- 方法一:
ALTER TABLE 表名 DROP CONSTRAINT 约束名;
-- 删除主键约束示例
ALTER TABLE COURSE DROP CONSTRAINT C_ID_PK;
-- 注意:要删除的主键约束必须起名
-- 方法二:
ALTER TABLE 表名 DROP PRIMARY KEY;
-- 删除主键约束示例
ALTER TABLE COURSE DROP PRIMARY KEY;
-- 注意:要删除的主键约束没有起别名,选用此方式
--语法规则:
alter table table_name modify column_name [datatype] null;
--操作示例:数据类型可以不写
ALTER TABLE COURSE MODIFY C_NAME NULL;
唯一性约束的注意事项:
方式一:创建表时添加唯一性约束
--创建学生表
CREATE TABLE STUDENT(
S_ID NUMBER(8,0) CONSTRAINT S_ID_PK PRIMARY KEY,
S_NAME VARCHAR2(20) NOT NULL,
S_SEX CHAR(2),
BIRTHDAY DATE,
ADDRESS VARCHAR2(50),
EMAIL VARCHAR2(50) UNIQUE,
CARDID VARCHAR2(18),
CONSTRAINT S_CARDID_PK UNIQUE(CARDID)
);
方式二:修改表添加唯一性约束
ALTER TABLE STUDENT ADD CONSTRAINT S_CARDID_PK UNIQUE(CARDID);
删除唯一性约束
--禁用约束
ALTER TABLE STUDENT DISABLE CONSTRAINT S_CARDID_PK;
--彻底删除
ALTER TABLE STUDENT DROP CONSTRAINT S_CARDID_PK;
方式一:创建表时添加检查约束
--创建学生表
--1.列级约束
CREATE TABLE STUDENT(
S_ID NUMBER(8,0),
S_NAME VARCHAR2(20) NOT NULL,
S_SEX CHAR(2) CHECK(S_SEX='男' OR S_SEX='女'),
BIRTHDAY DATE,
ADDRESS VARCHAR2(50),
EMAIL VARCHAR2(50),
CARDID VARCHAR2(18)
);
--2.表级约束
CREATE TABLE STUDENT(
S_ID NUMBER(8,0),
S_NAME VARCHAR2(20) NOT NULL,
S_SEX CHAR(2),
BIRTHDAY DATE,
ADDRESS VARCHAR2(50),
EMAIL VARCHAR2(50),
CARDID VARCHAR2(18),
CONSTRAINT CK_SEX CHECK(S_SEX='男' OR S_SEX='女')
);
方式二:修改表添加检查约束
ALTER TABLE STUDENT ADD CONSTRAINT CK_SEX CHECK(S_SEX='男' OR S_SEX='女') ;
删除唯一性约束
--禁用约束
ALTER TABLE STUDENT DISABLE CONSTRAINT CK_SEX;
--彻底删除
ALTER TABLE STUDENT DROP CONSTRAINT CK_SEX;
外键约束的注意事项:
方式一:创建表时添加外键约束
--列级约束
CREATE TABLE 从表 (Column_name datatype REFERENES)主表 (Column_name) [ON DELETE CASCADE],...);
--级联删除
ON DELETE CASCADE
--主表
CREATE TABLE DEP (
D_ID VARCHAR2(10) PRIMARY KEY,
D_NAME VARCHAR2(30)
);
--插入数据
INSERT INTO DEP VALUES ('744','计算机系');
--从表
CREATE TABLE STUDENT(
S_ID NUMBER(8,0),
S_NAME VARCHAR2(20),
SEX CHAR(2),
SD_ID VARCHAR2(10) REFERENCES DEP(D_ID)
ON DELETE CASCADE
);
--表级约束
CREATE TABLE 约束名 FOREIGN KEY REFERENES 主表 (Column_name) [ON DELETE CASCADE];
--从表
CREATE TABLE STUDENT(
S_ID NUMBER(8,0),
S_NAME VARCHAR2(20),
SEX CHAR(2),
SD_ID VARCHAR2(10),
CONSTRAINT fk_cid FOREIGN KEY(SD_ID) REFERENCES DEP(D_ID)
ON DELETE CASCADE
);
方式二:修改表添加约束
ALTER TABLE student ADD CONSTRAINTS fk_cid FOREIGN KEY(SD_ID) REFERENCES DEP(D_ID) ON DELETE CASCADE;
删除外键约束的方式
--禁用外键约束
ALTER TABLE STUDENT DISABLE CONSTRAINT fk_cid;
--彻底删除
ALTER TABLE STUDENT DROP CONSTRAINT fk_cid;
--语法结构:
ALTER TABLE 表名 ADD 新增例名 数据类型;
--示例
ALTER TABLE STUDENT ADD S_NUMBER CHAR(11);
--语法结构
ALTER TABLE 表名 MODIFY 列名 新数据类型;
--示例
ALTER TABLE STUDENT MODIFY S_NUMBER CHAR(12);
--语法结构
ALTER TABLE 表名 DROP 列名 ;
--示例
ALTER TABLE STUDENT DROP COLUMN S_NUMBER;
--语法结构
ALTER TABLE 表名 RENAME 列名 TO 新列名 ;
--示例
ALTER TABLE STUDENT RENAME COLUMN S_NUMBER TO S_TEL;
--语法结构
RENAME 表名 TO 新表名;
--示例
RENAME STUDENT TO STU;
--删除表数据
TRUNCATE TABLE 表名;
--示例
TRUNCATE TABLE STU;
--删除表结构
DROP TABLE 表名;
--示例
DROP TABLE STU;
注意:TRUNCATE操作用于删除表中的全部数据,并不是把表删除掉,这种方式要比delete方式删除数据的速度要快,也叫截断表
#语法结构
#方式一:
INSERT INTO 表名 [(列1,列2,...,列N)] VALUES (值1,值2,...,值N);
#示例
INSERT INTO DEP(D_ID,D_NAME) VALUES ('794','计算机系');
#方式二:
INSERT INTO TABLE_NAME(COLUNMN1,COLUNMN2...) SELECT VALUE1,VALUE2... FROM DUAL;
注意:字段要一一对应,否则报错!
#方式三:
insert into tableName(column1(主键),column2,column3...)
select tableNames_seq.nextval,,column2,column3... from (
select value1 column2,value2 column3,value3 column4 from dual
union
select value1 column2,value2 column3,value3 column4 from dual
union
select value1 column2,value2 column3,value3 column4 from dual
union
select value1 column2,value2 column3,value3 column4 from dual
)
#语法格式
SELECT * | COLUMN[,...] FROM 表名;
#示例
SELECT * FROM STU;
SELECT D_ID,D_NAME FROM DEP;
#语法格式
UPDATE TABLE SET COLUMN=VALUE [,COLUMN=VALUE,...] [WHERE CONDITION];
#示例 更新全部数据
UPDATE STU SET S_ID='781';
#示例 更新条件 S_NAME='李四'的数据
UPDATE STU SET S_ID='781' WHERE S_NAME='李四';
#语法格式
DELETE FROM TABLE [WHERE CONDITION];
#示例 删除全部数据
DELETE FROM STU;
#示例 删除条件 S_ID='781'的数据
DELETE FROM STU WHERE S_ID='781';
DML 语句需要使用COMMIT提交事务或使用ROLLBACK回滚事务
DDL和DCL是自动提交事务的
当执行事务操作(DML语句)时,Oracle会在被作用表上加表锁,以防止其他用户改变结构;同时会在被作用行上加行锁,以防止其他事务在相应行上执行DM操作。
提交事务(COMMIT)
回滚事务:(ROLLBACK)
保存点(SAVEPOINT)
--设置保存点:
SAVEPOINT a;
--回滚部分事务:
ROLLBACK TO a;
--回滚全部事务:
ROLLBACK;
常用数据字典分为三类:
user_*:
--该视图贮存了关于当前用户所拥有的对象的信息。
all_*:
--该视图贮存了当前用户能够访问的对象的信息。(不需要拥有该对象,有相应权限即可)
dba_*:
--该视图贮存了数据库所有的对象的信息。(前提是当前用户拥有访问这些数据库的权限,一般必须具有管理员权限)
数据字典的名称 | 说明 |
---|---|
user_users | 关于用户的信息 |
user_tablespaces | 关于表空间的信息 |
user_tables | 关于数据库表的信息 |
user_views | 关于视图的信息 |
user_sequences | 关于用户序列的信息 |
user_constraints | 关于用户表的约束信息 |
user_triggers | 关于用户的触发器信息 |
use_source | 关于用户储存过程的信息 |
应用数据字典查看相应的数据库信息
--查看当前用户下的用户信息
SELECT * FROM user_users;
--查看当前用户有权访问的所有用户的基本信息
SELECT * FROM all_users;
--数据库用户的所有信息
SELECT * FROM dba_users;
--语法格式
SELECT * | {[distinct]} column | expression [alias],...} FROM table [WHERE condition]
--解释
table --用于指定表名
column --用于指定列名
expression --用于指定表达式
alias --用于指定列的别名
condition --用于指定查询条件
distinct--去除重复行
--示例
SELECT DISTINCT DEPTNO,JOB FROM EMP;
select distinct deptno,job from emp;
(3).查询日期列
日期格式中RR与YY的区别
RR指定日期 | 当前年份 | RR格式说明 | RR结果年份 | YY格式说明 | YY结果年份 |
---|---|---|---|---|---|
18-1月-12 | 2017 | 指定年份在049之间 当前年份在049之间 |
2012 | 当前年份的前两位+指定日期的后两位 | 2012 |
18-1月-81 | 2017 | 指定年份在5099之间 当前年份在049之间 |
1981(上一个世纪) | 同上 | 2081 |
18-1月-12 | 2060 | 指定年份在049之间 当前年份在5099之间 |
2112(下一个世纪) | 同上 | 2012 |
18-1月-81 | 2060 | 指定年份在50~99之间 当前年份在50~99之间 |
2081 | 同上 | 2081 |
注意:RR中,指定日期中的年份和当前年份在049之间,表示结果年份和YY结果年份相同;指定日期中的年份在5099之间和当前年份在049之间,表示结果年份比YY结果早一个世纪;指定日期中的年份在049之间和当前年份在50~99之间,表示结果年份比YY结果晚一个世纪。
设置不同语言显示日期结果
--设置简体中文的日期格式
alter session set nls_language = 'SIMPLIFIED CHINESE';
select empno,ename,hiredate from emp;
--设置美国英语的日期格式
alter session set nls_language = 'AMERICAN';
select empno,ename,hiredate from emp;
--设置特定格式
alter session set nls_date_format='YYYY/MM/DD';
select empno,ename,hiredate from emp;
运算符 | 说明 |
---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
算术运算符可应用在数字和日期列上
--示例
select empno,ename,sal,sal*12 from emp;
算术运算符的优先级:
NULL:表示未知值,它既不是空格也不是0。
(1).当算数表达式包含NULL时,其显示结果也为空(NULL)
--例:查询编号、雇员、工资、补助、实发工资的信息。
select empno, ename, sal, comm, sal+comm from emp;
扩展知识:空值与默认值
空值:当插入数据时,如果没有为特定列提供数据,并且该列没有默认值,那么其结果为NULL。
默认值:在创建表时可以使用default关键字为列设置默认值,在表中插入数据时,如果没有为该列提供数据,那么该列将使用默认值。
--示例
create table student2(
sid number(8,0) primary key,
name varchar2(10),
sex char(2) default '男',
age number(2,0) default 20,
address varchar2(50));
SELECT * FROM student2;
insert into student2(SID,NAME) values(20010005,'王五');
insert into student2 values(24010002,'王二',DEFAULT,21,'北京市昌平区');
--查看Oracle的字符编码集
select userenv('language') from dual;
select * from nls_database_parameters WHERE parameter ='NLS_CHARACTERSET'
Oracle字符集知识
Oracle字符集是一个字节数据的解释的符号集合,有大小之分,有相互的包容关系。ORACLE 支持国家语言的体系结构允许你使用本地化语言来存储,处理,检索数据。它使数据库工具,错误消息,排序次序,日期,时间,货币,数字,和日历自动适应本地化语言和平台
--影响Oracle数据库字符集最重要的参数是NLS_LANG参数。
--它的格式如下:
NLS_LANG = language_territory.charset
--它有三个组成部分(语言、地域和字符集),每个成分控制了NLS子集的特性。
--其中:
--Language: 指定服务器消息的语言, 影响提示信息是中文还是英文
--Territory: 指定服务器的日期和数字格式,
--Charset: 指定字符集。
--如:AMERICAN _ AMERICA. ZHS16GBK
--从NLS_LANG的组成我们可以看出,真正影响数据库字符集的其实是第三部分
--查看数据库版本
select * from v$version;
--包含版本信息,核心版本信息,位数信息(32位或64位)等 至于位数信息,在Linux/unix平台上,可以通过file查看,如file $ORACLE_HOME/bin/oracle
--查看数据库字符集
--1.数据库服务器字符集
--方式一:
select * from nls_database_parameters;
--方式二:
select userenv('language') from dual;
--其来源于props$,是表示数据库的字符集。
--查看本地的字符集
select * from V$NLS_PARAMETERS
--2.客户端字符集
select * from nls_instance_parameters;
--其来源于v$parameter,表示客户端的字符集的设置,可能是参数文件,环境变量或者是注册表
--3.会话字符集
select * from nls_session_parameters;
--其来源于v$nls_parameters,表示会话自己的设置,可能是会话的环境变量或者是alter session完成,如果会话没有特殊的设置,将与nls_instance_parameters一致。
--4.dmp文件的字符集
select nls_charset_name(to_number('0354','xxxx')) from dual;
注意:如果别名中包含空格或特殊的字符或者需要区分大小写,那么需要给别名加上双引号
--不使用列名
select empno,ename,sal*12 from emp;
--使用列名
select empno "雇员编号", ename "雇员名", sal*12 "年收入" from emp;
select empno "雇员编号", ename "雇员名", sal*12 年收入 from emp;
select empno "雇员编号", ename "雇员名", sal*12 as 年收入 from emp;
select empno 雇员编号, ename 雇员名, sal*12 as 年收入 from emp;
当执行查询结果操作时,为了显示更有意义的结果值,有时需要将多个字符串连接起来,连接字符串可以使用"||"操作符或者concat函数,把列与列,列与字符连接在一起,用“||”表示,可以用来合并列。
--举例:
select ename||'的月工资是:'||sal||'岗位是:'||job as 雇员职位信息 from emp;
--错误示例
select ename || "1234" || sal from emp;
--正确示例
select ename || '1234' || sal from emp;
--语法规则:
SELECT * |{[DISTINCT] COLUMN | EXPRESSION [ALIAS],...} FROM TABLE [WHERE CONDITION(S)];
--数学值的情况
--说明:可直接引用数字值,也可以用单引号引注数学值
select * from emp where deptno=20;
select * from emp where deptno='20';
--字符值
--说明:使用字符值,必须给字符值加上单引号,字符值是区分大小写的
select * from emp where job='MANAGER';
--日期值的情况
--说明:使用日起值时,必须给日期值加上单引号,日期值必须符合日期语言和显示格式
select * from emp where hiredate = '02-4月-81';
比较运算符
运算符 | 说明 |
---|---|
= | 等于不是(==) |
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
!= | 不等于<> |
--操作示例
select * from emp where sal = 3000;
select * from emp where sal >= 3000;
select * from emp where sal < 3000;
select * from emp where sal <> 3000;
select * from emp where sal != 3000;
select * from emp where sal>1000 and sal<3000;
select * from emp where sal<1000 or sal>3000;
--用法
BETWEEN 较小值 AND 较大值
--示例
select empno,ename,sal from emp where sal between 1500 and 3000;
--等价于
select empno,ename,sal from emp where sal >= 1500 and sal<=3000;
--语法用法
IN(值1,[值2, ...值n])
--示例
select empno,ename, job from emp where job in ('SALESMAN','MANAGER','CLERK');
--等价于
select empno,ename, job from emp where job ='SALESMAN' OR job ='MANAGER' OR job ='CLERK';
通配符知识:
通配符 | 作用 |
---|---|
% | 用于表示0个或多个字符 |
_ | 用于表示单个字符 |
--用法
LIKE 执行模糊查询。
--示例
--查询以名字J开头的雇员信息
select * from emp where ename like 'J%';
--查询名字第2-3个字母是AR的雇员信息
select * from emp where ename like '_AR%';
--模糊查询中特殊符号的处理
--如果要查询的字符本身就包含_和%,必须使用escape选项和转义符实现。escape指明"\"说转义字符
--回避特殊符号的方法:使用转义符。例如:将[%]转为[\%]、[_]转为[\],然后再加上[ESCAPE'\']即可。
INSERT INTO emp(empno,ename,job) VALUES (7309,'G_JJJ','CLERK');
--查询以名字以G_开头的雇员信息
select * from emp where ename like 'G\_%' escape '\';
--用法
IS NULL操作符用于检测列或表达式的结果是否为NULL,如果为NULL,则返回true,反之返回false。
判断列或表达式的结果是否为NULL,可用IS NULL 或IS NOT NULL,但是不能=NULL或!=null来判断。
--示例
select empno, ename, sal, comm from emp where comm is null;
select empno, ename, sal, comm from emp where comm is not null;
操作符 | 说明 |
---|---|
AND | 逻辑与(并) |
OR | 逻辑或 |
NOT | 逻辑否 |
--说明:当执行SQL操作时,如果SQL语句结果必须同时满足多个条件,那么需要使用逻辑操作符AND。
--示例
select empno,ename,job,deptno from emp where job='MANAGER' AND deptno= 10;
--说明:当执行SQL操作时,如果SQL语句结果只须同时满足多个条件中的任意一个,那么需要使用逻辑操作符OR。
--示例
select empno, ename, job, sal from emp where job='MANAGER' or sal>2000;
--说明:当执行SQL操作时,如果SQL语句需要返回不满足特定条件的结果,那么需要使用逻辑操作符NOT。
--示例
select empno, ename,job from emp where job not in ('CLERK','SALESMAN','MANAGER');
逻辑操作符的优先级
NOT优先级最高,AND其次,OR优先级最低,如果需要改变优先级,需要使用括号。
select empno,ename,job,sal,deptno from emp where (sal > 2000 or deptno = 30) and job not in('PRESIDENT','MANAGER');
--语法规则:
SELECT *|column1[,column2,...] from table_name [where condition] [order by column [ASC|DESC]];
--ASC:升序 DESC:降序
注意:当select语句包含多个子句(where,group by,having,order by)时,order by 必须是最后一条语句。
--单列排序
--单列升序
--说明:如果排序列存在NULL行,那么NULL行会显示在最后面
select ename,sal from emp order by sal ASC;
select ename,sal from emp order by sal;(默认升序)
--单列降序
--说明:如果排序列存在NULL行,那么会显示在最前面
select ename,sal from emp order by sal DESC;
--多列排序
--说明:当执行排序操作时,首先按照第一列进行排序,当第一列存在相同的数据时,以第二列进行排序,以此类推。
select ename,empno,deptno,sal from emp order by depntno asc,sal desc;
--使用列别名排序
select empno,ename,sal*12 年收入 from emp ORDER BY 年收入;
SQL:
SQLPLUS:
passw[ord] [username]
,username用于指定用户名,注意:任何用户都可使用该命令修改其他自身口令,但如果要修改其他用户口令,则必须以DBA的身份登入(sys/system)函数类型 | 作用 |
---|---|
大小写控制函数 | |
UPPER(char) | 将字符串转换为大写格式 |
LOWER(char) | 将字符串转换为小写格式 |
INITCAP(char) | 将字符串中的每个单词的首字母大写 |
连接函数 | |
CONCAT(str1,str2) | 将str1,str2进行拼接成一个新的字符串 |
截取函数 | |
SUBSTR(char,m[n]) | 用于截取字符串,char指定被截取的字符串,m用于指定从哪个位置开始截取,n用于指定截取字符串的长度,注意:m为-1,代表从尾部开始截取,m为大于0,则从首字符下标对应索引开始,索引默认从1开始,但m=0,则是从首字符开始截取 |
填充函数 | |
LPAD(char1,n,char2) | 用于在字符串的左端填充字符,char1用于指定源字符串,char2用于指定被填充的字符,n用于指定填充后的char1的总长度 |
LPAD(char1,n,char2) | 用于在字符串的右端填充字符,char1用于指定源字符串,char2用于指定被填充的字符,n用于指定填充后的char1的总长度 |
长度函数 | |
LENGTH(char) | 用于返回字符串的长度,字符串中的后缀空格也记作字符串的长度。 |
查看字符出现的索引位置 | |
INSTR(char1,char2[,n[m]]) | 用于取得子串在字符串中的位置,char1指定源字符串,char2指定子串,n指定起始开始搜索位置(默认值为1),m用于指定子串的第m次出现的次数(默认值为1) |
替换字符串函数 | |
REPLACE(char,search_string[,replacement_string]) | 用于替换字符串的子串内容,Char用于指定源字符串,search_string用于指定被替换,replacement_string用于指定替换子串 |
操作示例:
-- length: 获取参数值的字节个数
SELECT LENGTH('john') FROM dual;
-- concat: 拼接字符串
SELECT CONCAT(empno,ename) FROM emp;
-- upper:大写 lower: 小写
SELECT UPPER('john') FROM dual;
SELECT LOWER('HELLO WORLD') FROM dual;
--initcap:首字母大写
select initcap('initCAP') from dual;
-- substr
-- 注意:索引从1开始
SELECT SUBSTR('李莫愁爱上了陆展元',6) FROM dual; -- 截取从指定索引处后面所有字符
-- instr: 返回子字符串在父字符串的起始索引,如果没有返回0
SELECT INSTR('杨不梅爱上了殷六霞','殷六霞') AS out_put FROM dual;
-- trim:
注意:只去除字符串中前后的字符或空格,不去除字符串中间的字符或空格
SELECT TRIM(' asdf ') ,--去两边空空格,
TRIM(' as df '), --去两边空空格,不去除中间空格
LTRIM(' asdf '), --去左边空格,
RTRIM(' asdf '), --去右边空格,
LTRIM('asddg','as')--去指定字符
FROM DUAL;
-- lpad: 用指定的字符实现左填充到指定长度
注意:当字符串长度大于给定的指定长度,则会从左边开始截取指定长度
SELECT LPAD('qzp',10,'*') FROM dual;
SELECT LPAD('qzp',2,'*') FROM dual;
-- rpad: 用指定的字符实现右填充指定长度
SELECT RPAD('qzp',12,'*') FROM dual;
SELECT RPAD('qzp',2,'*') FROM dual;
-- replace: 替换,选取需要被替换的字符,用指定的字符代替
SELECT REPLACE('qzpxihuanwjw','qzp','wo') FROM dual;
--TRANSLATE:逐个替换,源字符串出现指定的字符串的字符,换成对应的
SELECT ENAME,
REPLACE(ENAME, 'A', '000'),--替换,
TRANSLATE(ENAME, 'ABCD', '0123') --逐个替换
FROM EMP;
函数类型 | 作用 |
---|---|
ROUND(n[,m]) | 用于返回四舍五入的结果,其中n可以任意数,m必须是整数 |
TRUNC(n[,m]) | 用于截取数字,其中n可以是任意数字,m必须是整数 |
MOD(m,n) | 用于取得两个数的相除后的余数,如果数字n为0,则返回结果为m |
-- round 四舍五入
-- 诀窍:取绝对值,再添加+-号
SELECT ROUND(1.65) FROM dual; -- 结果:2
SELECT ROUND(-1.65) FROM dual; -- 结果:-2
-- 保留指定的小数点位数,正数表示小数点向右保留对应位数,负数表示小数点向左保留对应位数,实际小数点不会移动
SELECT ROUND(11.567,1) FROM dual; -- 结果:11.6
SELECT ROUND(11.567,-1) FROM dual; -- 结果:10
-- trunc 截取数字
SELECT TRUNC(25.329) FROM dual; --结果是:25
SELECT TRUNC(25.329,2) FROM dual; --结果是:25.33
SELECT TRUNC(25.329,-1) FROM dual; --结果是:20
-- mod(被除数,除数) 取余
-- 诀窍:mod(a,b)==>a-a/b*b;被除数为正,结果则为正数,被除数为负数,结果则为负数
SELECT 10%3;
SELECT MOD(10,3) FROM dual; --结果:1
SELECT MOD(-10,-3) FROM dual; --结果:-1
SELECT MOD(-10,3) FROM dual; --结果:-1
SELECT MOD(10,-3) FROM dual; --结果:1
--SYSDATE:用于返回当前系统日期
select sysdate from dual;--返回当前系统时间
select sysdate-1 昨天,sysdate 今天,sysdate+1 明天 from dual;
--MONTHS_BETWEEN(d1,d2):用于返回日期d1和d2之间相差的月数,d1大于d2结果为正数,否则为负数
select months_between(sysdate,hiredate) from emp;--查询工人的工作总月份
select round(months_between(sysdate,hiredate)/12) from emp;--查询工人的工作年限
--ADD_MONTHS(d,n):用于返回特定日期时间之后或之前的月份对应的日期时间,d用于指定日期时间,n可以是任意整数
注意:n为正整数,为当前月之后的月份,n为负数,为当前月之前的月份
select ename,add_months(hiredate,30*12) from emp;--查询每位员工入职30年后的年份
--NEXT_DAY(d,char):用于返回特定日期之后的第一个工作日多对应的日期,d用于指定日期时间值,char用于指定工作日
注意:当使用该函数时,工作日必须与日期语言匹配,假如日期语言为AMERICAN,那么周一对应于"MONDAY";日期语言为简体中文,那么周一对应于"星期一"
select sysdate,next_day(sysdate,'星期一') from dual;--查询下周一的日期
--LAST_DAY(d):用于返回特定日期所在月份的最后一天
select sysdate,last_day(sysdate) from dual;--查询当前日期所在月份的最后一天
--ROUND(d[,fmt]):用于返回日期时间的四舍五入的结果。d用于指定日期的时间值,fmt用于指定四舍五入的方式
注意:如果设置fmt为YEAR,则7月1日为分界线,如果设置fmt为MONTH,则16日为分界线
--如系统时间是'2020-10-09'
select round(sysdate,'year') from dual;--结果为:2021/1/1
select round(sysdate,'month') from dual;--结果为:2020/10/1
--TRUNC(d[,fmt]):用于截断日期时间数据,d用于指定日期的时间值,fmt用于指定截断日期时间数据的方法
注意:如果设置fmt为YEAR,则结果为本年度的1月1日,如果设置fmt为MONTH,则结果为本月1日
SELECT TRUNC(SYSDATE,'year') FROM dual;--结果:2020/1/1
SELECT TRUNC(SYSDATE,'month') FROM dual;--结果:2020/10/1
Oracle可以隐式的(自动的)进行数据类型转换
源数据类型 | 目标数据类型 |
---|---|
VARCHAR2或CHAR | NUMBER |
VARCHAR2或CHAR | DATE |
NUMBER | VARCHAR2 |
DATE | VARCHAR2 |
select * from emp where sal > '100';
select * from emp where hiredate = '17-12月-80';
转换格式:
格式字符串 | 含义 |
---|---|
YYYY/MM/DD | 年/月/日 |
YYYY | 年(4位) |
YYY | 年(3位) |
YY | 年(2位) |
MM | 月份 |
DD | 日期 |
D | 星期 |
DDD | 一年之第几天 |
WW | 一年之第几周 |
W | 一月之第几周 |
YYYY/MM/DD HH24:MI:SS | 年/月/日时(24小时制):分:秒 |
YYYY/MM/DD HH:MI:SS | 年/月/日时(非24小时制):分:秒 |
--函数格式:
TO_CHAR(d[,fmt[,'nlsparams']])
--d用于指定日期值,fmt用于指定日期格式模型,'nlsparams'用于指定日期显示语言(格式:'NLS_LANGUAUE=language')
--默认日期显示格式为DD-MON-RR
select to_char(hiredate,'DD-MON-RR','NLS_LANGUAUE=AMERICAN') FROM EMP;
注意:当在格式模型中增加字符值时,必须用双引号引注字符值
select to_char(hiredate,'YYYY"年"MM"月"DD"日"') from emp;
--函数格式:
TO_CHAR(d[,fmt])
--n用于指定数值,fmt用于指定数字格式的模型
格式模型,常用的元素如下:
9:显示数字,并且会忽略前导0
0:显示数字,如果位数不足,则用0补齐
.:在指定位置显示小数点
,:在指定位置显示逗号
$:在数字前加美元符号
L:在数字前加本地货币符号
--数字转换成字符
select sal,to_char(sal,'L0,000,000.00') from emp;--显示本地货币本身,位数不够,补零
select sal,to_char(sal,'L9,999,999.99') from emp;--显示本地货币本身,位数不够,空着
select sal,to_char(sal,'$9,999,999.99') from emp;--显示美元货币本身,位数不够,空着
--语法格式;
TO_DATE(char[,fmt[,'nlsparams']])
--char用于匹配日期数据的字符串,fmt用于指定日期格式模型,'nlsparams'用于指定日期语言
--查看1982之后入职的员工信息
select ename,hiredate from emp where hiredate>to_date('1981-12-31','YYYY-MM-DD');
TO_NUMBER
--语法格式:
TO_NUMBER(n[,fmt])
--n是包含数字的字符串,fmt用于指定数字格式模型
--显示工资大于2000的所有员工的姓名和工资
select ename,sal from emp where sal>to_number('¥2000','L99999');
--语法格式
NVL(expr1,expr2)
--如果expr1是null,则返回expr2,如果expr1不是null,则返回expr1
select ename,sal,comm,sal+nvl(comm,0) from emp;
--语法格式
NVL2(expr1,expr2,expr3)
--如果expr1不是null,则返回expr2,如果expr1是null,则返回expr3
select ename,sal,comm,nvl2(comm,sal+comm,sal) from emp;
--语法格式
NULLIF(expr1,expr2)
--如果表示式expr1不等于表达式expr2,返回expr1,如果两个表达式相等,则返回null
select empno,ename,hiredate,nullif(hiredate,trunc(sysdate,'MONTH')) from emp;
--语法格式
COALESCE(expr1[,expr2][,...])
--如果表示式expr1不等于表达式expr2,返回expr1,如果两个表达式相等,则返回null
select ename,sal,comm,coalesce(sal+comm,sal) from emp;
-- case函数
--case函数的使用方式一:
/*
CASE 要判断的字段或表达式1
WHEN 常量1 THEN 显示的值1或者语句1;
WHEN 常量2 THEN 显示的值2或者语句2;
WHEN 常量3 THEN 显示的值3或者语句3;
....
ELSE 显示的值或者语句n;
END
*/
# 注意:语句需要;,显示值不需要;类似于java中的switch-case用法
# 代码实例
SELECT * ,CASE degree
WHEN 90 THEN '优秀'
WHEN 80 THEN '良好'
WHEN 70 THEN '一般'
WHEN 60 THEN '及格'
ELSE '不合格'
END FROM score;
# case函数的使用方式二:类似于多重if
/*
CASE
WHEN 条件1 THEN 显示的值1或者语句1;
WHEN 条件2 THEN 显示的值1或者语句1;
WHEN 条件3 THEN 显示的值1或者语句1;
WHEN 条件4 THEN 显示的值1或者语句1;
ELSE 显示的值或者语句n;
END
*/
# 注意:语句需要;,显示值不需要;
# 代码实例:
SELECT * ,CASE
WHEN degree>=90 THEN '优秀'
WHEN degree>=80 AND degree <90 THEN '良好'
WHEN degree>=70 AND degree <80 THEN '一般'
WHEN degree>60 AND degree <70 THEN '及格'
ELSE '不合格'
END AS 成绩水平 FROM score;
--decode函数
--方式一使用语法规则
select 列名,decode(col|expression,search1,result1,[,search2,result2,..][,default])
select empno,ename,job,decode(job,'CLERK','办事员','SALESMAN','销售','MAMAGER','经理','ANALYST','分析员','总裁') from emp;
--方式二使用语法规则
select 列名,decode(列名,值1,数据1,....值n,数据n,默认数据)from table_name;
create table kecheng
(
id NUMBER,
name VARCHAR2(20),
course VARCHAR2(20),
score NUMBER(3,1) --一位小数的数据
);
--插入数据
insert into kecheng VALUES (1, '张三', '语文', 67);
insert into kecheng (id, name, course, score)
values (1, '张三', '数学', 76);
insert into kecheng (id, name, course, score)
values (1, '张三', '化学', 45);
insert into kecheng (id, name, course, score)
values (2, '李四', '语文', 54);
insert into kecheng (id, name, course, score)
values (2, '李四', '数学', 81);
insert into kecheng (id, name, course, score)
values (2, '李四', '英语', 64);
insert into kecheng (id, name, course, score)
values (2, '李四', '历史', 93);
insert into kecheng (id, name, course, score)
values (2, '李四', '化学', 27);
insert into kecheng (id, name, course, score)
values (3, '王五', '语文', 24);
insert into kecheng (id, name, course, score)
values (3, '王五', '数学', 25);
insert into kecheng (id, name, course, score)
values (3, '王五', '英语', 8);
insert into kecheng (id, name, course, score)
values (3, '王五', '历史', 45);
insert into kecheng (id, name, course, score)
values (3, '王五', '化学', 1);
insert into kecheng (id, name, course, score)
values (1, '张三', '英语', 43);
insert into kecheng (id, name, course, score)
values (1, '张三', '历史', 56);
commit;--提交事务
SELECT * FROM kecheng;
select ID,NAME,MAX(DECODE(course,'语文',score))语文,
MAX(DECODE(course,'数学',score))数学,
MAX(DECODE(course,'英语',score))英语,
MAX(DECODE(course,'化学',score))化学,
MAX(DECODE(course,'历史',score))历史,
from kecheng group by ID,NAME;
select wm_concat(ename) from emp;
--------------------------------行列转换-----------------------------------------
有的时候吧查询结果集做成列的效果,这样展示出来会更加直观;
行转列有四种方法 CASE WHEN 、DECODE 、PIVOT 、wm_concat;
SELECT ID, NAME FROM KECHENG GROUP BY ID, NAME HAVING MIN(SCORE) > 40;
--1.case WHEN 判断条件, 每个case when会在结果集里面多增加一列
SELECT ID,NAME ,max(CASE WHEN course='语文' THEN score END) 语文,
min(CASE WHEN course='数学' THEN score END) 数学,
sum(CASE WHEN course='化学' THEN score END) 化学,
SUM( CASE WHEN course='历史' THEN score END) 历史,
SUM( CASE WHEN course='英语' THEN score END) 英语 FROM kecheng GROUP BY ID,NAME ORDER BY ID;
--2.decode 判断指定的值,decode能干的时候case when一定可以做;
语法 SELECT 列名,DECODE(列名,值1,数据1,...值n,数据n,默认数据) FROM 表
SELECT ID,NAME,MAX(DECODE(course,'语文',score))语文,
MAX(DECODE(course,'数学',score))数学,
MAX(DECODE(course,'英语',score))英语,
MAX(DECODE(course,'化学',score))化学,
MAX(DECODE(course,'历史',score))历史 FROM kecheng GROUP BY ID,NAME;
SELECT emp.*,DECODE(job,'CLERK','小助理','MANAGER','经理','SALESMAN','销售','其他工作') 中文名称 FROM emp;
--3.pivot oracle 11版本才有的
语法:SELECT * FROM 表 PIVOT( 聚合函数(数据列名) FOR 要转的列名 IN (新列名的集合) )
语法: SELECT 你需要的列名 FROM 表 UNPIVOT( 数据 FOR 要转的列名 IN (原表老的列名集合) )
--for是循环的意思,for是遍历的意思。
SELECT * FROM kecheng PIVOT(SUM(score) FOR course IN ('语文' 语文 ,'数学' 数学 , '英语' 英语 ,'化学' 化学 ,'历史' 历史));
--4.wm_concat concat拼接的意思
SELECT ID,name,replace(wm_concat(course||score),',',' ') 详细信息 FROM kecheng GROUP BY ID,NAME;
分组函数的语法:
select [column,] group function(column)...
from table_name
[where condition]
[group by group_by_expression]
[having group_condition]
[order by column];
常用的分组函数:
函数 | 作用 | 支持参数类型 |
---|---|---|
SUM(字段名) | 求和 | 数值型 |
AVG(字段名) | 求平均值 | 数值型 |
MAX(字段名) | 求最大值 | 任何类型 |
MIN(字段名) | 求最小值 | 任何类型 |
COUNT(*) COUNT(1) COUNT(字段名) |
统计,不过滤null值 统计,不过滤null值 统计,过滤null值 |
任何类型 |
--求最大值
select max(sal) from emp;
--求最小值
select min(sal) from emp;
--求平均值
select avg(sal) from emp;
--求和
select sum(sal) from emp;
COUNT(*)和COUNT(1):使用的时候,不会自动过滤null值(简单理解,COUNT(星)是统计行记录,只要这行记录至少有一个字段不为空,就可以+1;COUNT(1)是相当于在表前面加了一列,字段内容为1,然后统计 1 的个数,来统计行数)
COUNT(字段名):使用的时候自动过滤null值
set运算符 | 作用 |
---|---|
union union all |
并集,去重 并集,不去重 |
intersect | 交集 |
minus | 差集 |
union:操作符用于取得两个结果集的并集,当使用该操作符是,会自动去掉结果集中的重复行,并且会以第一列的字段进行升序排序
union all :操作符用于取得两个结果集的并集,但与union操作符不同,该操作符不会取消重复行,并且不会对结果集数据进行排序
intersect:操作符用于取得两个结果集的交集,当使用操作符时,只会显示同时存在于两个结果集中的数据,并且会以第一列的字段进行升序排序。
minus :操作符用于取得两个结果集的差集,当使用该操作符时,只会显示在第一个结果集中存在,在第二个结果集中不存在的数据,并且会以第一列的结果集进行升序排序。
--复制两张表
create table emp01 as select * from emp;
create table emp02 as select * from emp;
--复制两张表结构,自己插入数据
create table emp03 as select * from emp where 1=2;
--union 14条数据
SELECT * FROM emp01
UNION
SELECT * FROM emp02;
--union all 28条数据
SELECT * FROM emp01
UNION ALL
SELECT * FROM emp02;
--intersect 14条数据
INSERT INTO emp01 VALUES(7333,'qzp',NULL,NULL,NULL,null,null,40);
SELECT * FROM emp01
INTERSECT
SELECT * FROM emp02;
--minus 1条数据,插入在emp01表中的一条
SELECT * FROM emp01
MINUS
SELECT * FROM emp02;
注意:order by 后面可以用数字代表字段的列数
SELECT * FROM emp ORDER BY 6;
------------------------------rownum------------------------------------
分页查询,从表里面提取指定的数据;mysql也有 limit 0,1
rownum实际上是不存在表里面,是查询出来之后,临时产生的一个序列号而已;rownum只能小于 不能大于
--查询前5条
SELECT emp.*,ROWNUM ru From emp WHERE ROWNUM<=5;
--查询3-8条
SELECT emp.*,ROWNUM ru From emp WHERE ROWNUM<=8
MINUS
SELECT emp.*,ROWNUM ru From emp WHERE ROWNUM<=2;
--子查询也可以
SELECT a.* FROM
(SELECT emp.*,ROWNUM ru From emp)A WHERE a.ru BETWEEN 3 AND 8;
------------------------------rownid------------------------------------
ROWID是物理地址,在数据插入的时候就已经写入到磁盘了,删除重复数据的时候使用,术语叫清洗数据; etl工程师 bi工程师 数据库开发工程师
SELECT emp.*,ROWID From emp ;
CREATE TABLE SS(ID number,NAME VARCHAR2(10));
INSERT INTO SS VALUES(111,'sadfasd');
INSERT INTO SS VALUES(111,'sadfasd');
INSERT INTO SS VALUES(22,'3333');
INSERT INTO SS VALUES(22,'3333');
COMMIT;--update insert delete 都要记得提交
SELECT SS.*,ROWID From SS;
--清洗数据的时候,重复的数据需要保留一条,筛选rowid的时候,选最大或者最小不重要
DELETE FROM SS WHERE ROWID NOT IN (SELECT MIN(ROWID) FROM SS GROUP BY ID,NAME);
--------------课堂练习-------------------------------
CREATE TABLE table1(编号 char(10),姓名 char(10));
INSERT INTO table1 VALUES('1','a');
INSERT INTO table1 VALUES('6','b');--
INSERT INTO table1 VALUES('8','c');--
INSERT INTO table1 VALUES('3','a');--
INSERT INTO table1 VALUES('3','c');
INSERT INTO table1 VALUES('5','c');
INSERT INTO table1 VALUES('2','b');
INSERT INTO table1 VALUES('3','b');
SELECT * FROM table1;
TRUNCATE TABLE table1;
---删除数据 保留相同姓名的 编号最大的一条数据
DELETE FROM table1 WHERE (编号,姓名) NOT IN (SELECT MAX(编号),姓名 FROM table1 GROUP BY 姓名)
DELETE FROM TABLE1
WHERE ROWID IN (SELECT a.ROWID
FROM TABLE1 A, TABLE1 B
WHERE A.姓名 = B.姓名
AND A.编号 < B.编号)
--扩展 补充重复数据的情况
INSERT INTO table1 VALUES('8','c');--
INSERT INTO table1 VALUES('3','a');--
DELETE FROM TABLE1
WHERE ROWID NOT IN
(SELECT MAX(ROWID)
FROM TABLE1
WHERE (编号, 姓名) IN (SELECT MAX(编号), 姓名 FROM TABLE1 GROUP BY 姓名)
GROUP BY 编号, 姓名)
----------------------------------------个人解答----------------------------------------------------------
--方式一:
SELECT MAX(编号),姓名 FROM table1 GROUP BY 姓名;
DELETE FROM table1 WHERE (编号,姓名) NOT IN(SELECT MAX(编号),姓名 FROM table1 GROUP BY 姓名);
--方式二:
SELECT table1.*,ROWID FROM table1;
SELECT a.ROWID
FROM table1 a,table1 b
WHERE a.姓名=b.姓名 AND a.编号 a.asal THEN 1 ELSE 0 END ) / count( empno ), 4 ) * 100 || '%'百分比
FROM
emp b join ( SELECT deptno, avg( sal ) asal FROM emp GROUP BY deptno ) a ON a.deptno = b.deptno
GROUP BY
a.deptno
ORDER BY
a.DEPTNO;
--分析,首先计算每个部门的平均工资,计算大于平均工资的人数,计算部门的总人数,
--方式二:
SELECT B.DEPTNO, ROUND((B.CT1 / C.CT2), 4) * 100 || '%'
FROM (SELECT E.DEPTNO, COUNT(E.EMPNO) CT1
FROM EMP E
JOIN (SELECT DEPTNO, AVG(SAL) PJ FROM EMP GROUP BY DEPTNO) A
ON E.DEPTNO = A.DEPTNO
AND E.SAL > A.PJ
GROUP BY E.DEPTNO) B
JOIN (SELECT DEPTNO, COUNT(EMPNO) CT2 FROM EMP GROUP BY DEPTNO) C
ON B.DEPTNO = C.DEPTNO;
--分析,首先计算每个部门的平均工资,总人数,计算大于平均工资的人数,
SELECT E.DEPTNO, COUNT(*) / MAX(B.CT1)
FROM EMP E
JOIN (SELECT DEPTNO, COUNT(EMPNO) CT1, AVG(SAL) PJ
FROM EMP
GROUP BY DEPTNO) B
ON E.DEPTNO = B.DEPTNO
AND E.SAL > B.PJ
GROUP BY E.DEPTNO;
--分析函数,直接对比自己的工资和平均工资,如果大于就写个1记录一下
SELECT A.DEPTNO,
SUM(CASE
WHEN A.SAL > A.PJ THEN
1
ELSE
0
END) / COUNT(EMPNO) 百分比
FROM (SELECT EMP.*, AVG(SAL) OVER(PARTITION BY DEPTNO) PJ FROM EMP) A
GROUP BY A.DEPTNO;
-----------------------------------练习---------------------------------------------
CREATE TABLE logasset(营业部 NUMBER(2),客户id NUMBER(4),产品 VARCHAR(2),销量 NUMBER(10));
INSERT INTO logasset VALUES (2,201,'A',10000);
INSERT INTO logasset VALUES (2,201,'B',30);
INSERT INTO logasset VALUES (2,203,'C',200);
INSERT INTO logasset VALUES (3,301,'A',200);
INSERT INTO logasset VALUES (3,302,'A',7000);
INSERT INTO logasset VALUES (3,303,'B',10000);
SELECT * FROM logasset;
1.计算每个营业部有效客户,标准金额>1000
SELECT A.营业部, COUNT(*) 有效客户数量
FROM (SELECT 营业部, 客户ID, SUM(销量)
FROM LOGASSET
GROUP BY 营业部, 客户ID
HAVING SUM(销量) > 1000) A
GROUP BY A.营业部;
SELECT A.营业部, COUNT(*) 数量
FROM (SELECT DISTINCT 营业部,
客户ID,
SUM(销量) OVER(PARTITION BY 营业部, 客户ID) XL
FROM LOGASSET) A
WHERE A.XL > 1000 GROUP BY A.营业部 ;
2.计算哪个产品销量最好,哪个最差
SELECT A.产品,
CASE
WHEN A.HIGH = 1 THEN
'最好'
else
'最差'
END 排名
FROM (SELECT 产品,
ROW_NUMBER() OVER(ORDER BY SUM(销量) DESC) HIGH,
ROW_NUMBER() OVER(ORDER BY SUM(销量)) LOWER
FROM LOGASSET
GROUP BY 产品) A
WHERE A.HIGH = 1
OR A.LOWER = 1;
3.计算每个营业部销量最好的产品
SELECT A.营业部, A.产品
FROM (SELECT 营业部,
产品,
SUM(销量),
ROW_NUMBER() OVER(PARTITION BY 营业部 ORDER BY SUM(销量) DESC) PM
FROM LOGASSET
GROUP BY 营业部, 产品)A
WHERE A.PM = 1;
---2种偏移
把指定的一列的数据 往前或者往后移动几位 LAG LEAD
一般用于单表列与列之间进行计算,对比
语法 SELECT 列名,LAG|LEAD(列名,偏移量,默认值(不写的时候就是null)) FROM 表
SELECT empno,ename,LAG(ename,2)OVER(ORDER BY empno) py FROM emp;
--查询一下emp里面 ,后面一个员工比前面一个员工晚入职多久?
SELECT EMP.*, LAG(HIREDATE, 1) OVER(ORDER BY EMPNO) - HIREDATE 晚入职
FROM EMP;
SELECT * From sc;
1.查询c001比c002成绩高的学号?
分析:首先过滤数据只保留c001 c002的课程信息,然后c001的分数偏移一位,跟c002在同一行了;再对比
SELECT A.SNO
FROM (SELECT SNO,
CNO,
SCORE,
LAG(SCORE, 1) OVER(PARTITION BY SNO ORDER BY CNO) C001
FROM SC
WHERE CNO IN ('c001', 'c002')) A
WHERE A.C001 > A.SCORE;
SELECT A.SNO
FROM (SELECT SNO,
CNO,
SCORE,
LEAD(SCORE, 1) OVER(PARTITION BY SNO ORDER BY CNO) C002
FROM SC
WHERE CNO IN ('c001', 'c002')) A
WHERE A.C002 < A.SCORE;
-----------------------------课堂练习----------------------------------------------
create table earnings -- 打工赚钱表
(
earnmonth varchar2(6), -- 打工月份
area varchar2(20), -- 打工地区
sno varchar2(10), -- 打工者编号
sname varchar2(20), -- 打工者姓名
times int, -- 本月打工次数
singleincome number(10,2), -- 每次赚多少钱
personincome number(10,2) -- 当月总收入
) ;
insert into earnings values('200912','北平','511601','大魁',11,30,11*30);
insert into earnings values('200912','北平','511602','大凯',8,25,8*25);
insert into earnings values('200912','北平','511603','小东',30,6.25,30*6.25);
insert into earnings values('200912','北平','511604','大亮',16,8.25,16*8.25);
insert into earnings values('200912','北平','511605','贱敬',30,11,30*11);
insert into earnings values('200912','金陵','511301','小玉',15,12.25,15*12.25);
insert into earnings values('200912','金陵','511302','小凡',27,16.67,27*16.67);
insert into earnings values('200912','金陵','511303','小妮',7,33.33,7*33.33);
insert into earnings values('200912','金陵','511304','小俐',0,18,0);
insert into earnings values('200912','金陵','511305','雪儿',11,9.88,11*9.88);
insert into earnings values('201001','北平','511601','大魁',0,30,0);
insert into earnings values('201001','北平','511602','大凯',14,25,14*25);
insert into earnings values('201001','北平','511603','小东',19,6.25,19*6.25);
insert into earnings values('201001','北平','511604','大亮',7,8.25,7*8.25);
insert into earnings values('201001','北平','511605','贱敬',21,11,21*11);
insert into earnings values('201001','金陵','511301','小玉',6,12.25,6*12.25);
insert into earnings values('201001','金陵','511302','小凡',17,16.67,17*16.67);
insert into earnings values('201001','金陵','511303','小妮',27,33.33,27*33.33);
insert into earnings values('201001','金陵','511304','小俐',16,18,16*18);
insert into earnings values('201001','金陵','511305','雪儿',11,9.88,11*9.88);
commit;
select * from earnings;
---按照月份、地区,求各个打工收入排序
--累计求和根据月份地区求出各个打工者收入总和,按照收入由少到多排序
--按照月份和地区求打工收入最高值,最低值,平均值和总额
-- 用lead lag 计算出来每个员工的上个月和下个月有没有赚钱。(personincome大于0就是赚了)
/*月份 姓名 上个月 下个月
200912 大魁 没赚 --相对于12月上个月就是11月,但是11月我们表里面没有数据,所以这个地方计算出来是空值;
201001 大魁 赚了 --相对于1月下个月就是2月,但是2月我们表里面没有数据,所以这个地方计算出来是空值;
200012 大凯 赚了
201001 大凯 赚了 */
select * from earnings;
---按照月份、地区,求各个打工收入排序
SELECT e.*,row_number()OVER(PARTITION BY e.earnmonth,e.area ORDER BY e.personincome ) PX FROM earnings e;
--累计求和根据月份地区求出各个打工者收入总和,按照收入由少到多排序
SELECT e.*,SUM(e.personincome)OVER(PARTITION BY e.earnmonth,e.area ORDER BY e.personincome ) PX FROM earnings e;
--按照月份和地区求打工收入最高值,最低值,平均值和总额
SELECT e.earnmonth,e.area, MAX(e.personincome) ,min(e.personincome) ,avg(e.personincome) ,sum(e.personincome) FROM earnings e
GROUP BY e.earnmonth,e.area;
SELECT e.earnmonth,e.area,
MAX(e.personincome)OVER(PARTITION BY e.earnmonth,e.area) ,
min(e.personincome)OVER(PARTITION BY e.earnmonth,e.area) ,
avg(e.personincome)OVER(PARTITION BY e.earnmonth,e.area) ,
sum(e.personincome)OVER(PARTITION BY e.earnmonth,e.area)
FROM earnings e ;
--用lead lag 计算出来每个员工的上个月和下个月有没有赚钱。(personincome大于0就是赚了)
/*月份 姓名 上个月 下个月
200912 大魁 没赚 --相对于12月上个月就是11月,但是11月我们表里面没有数据,所以这个地方计算出来是空值;
201001 大魁 赚了 --相对于1月下个月就是2月,但是2月我们表里面没有数据,所以这个地方计算出来是空值;
200012 大凯 赚了
201001 大凯 赚了 */
SELECT EARNMONTH,
SNAME,
PERSONINCOME,
LAG(decode(PERSONINCOME,0,'没赚','赚了'), 1) OVER(PARTITION BY SNAME ORDER BY EARNMONTH) 上个月,
LEAD(decode(PERSONINCOME,0,'没赚','赚了'), 1) OVER(PARTITION BY SNAME ORDER BY EARNMONTH) 下个月
FROM earnings;
SELECT EARNMONTH,SNAME,
(CASE WHEN 上个月>0 THEN '赚了' WHEN 上个月=0 THEN '没赚' ELSE NULL END) 上个月,
(CASE WHEN 下个月>0 THEN '赚了' WHEN 下个月=0 THEN '没赚' ELSE NULL END) 下个月
FROM
(SELECT
EARNMONTH,
SNAME,
PERSONINCOME,
LAG(PERSONINCOME, 1) OVER(PARTITION BY SNAME ORDER BY EARNMONTH)上个月,
LEAD(PERSONINCOME, 1) OVER(PARTITION BY SNAME ORDER BY EARNMONTH)下个月
FROM EARNINGS)
示例:
SELECT *
from emp
where job =(select job
from emp
where ename ='SMITH');
--查询和 SMITH是同一个职位的员工。
--第一步:查询 SMITH的职位
select job from emp where ename='SMITH';
--第二步:查询和 SMITH是同一个职位的员工
select * from emp where job = 'CLERK';
---语法结构
SELECT select list
FROM table
WHERE expr operator--主查询
(SELECT select_list
FROM table);--子查询
--可以使用子查询的位置: where, select, having,from
--举例:
--查询出每个部门的编号,名称,位置,部门人数
select deptno,dname,loc, (select count(empno) from emp where emp.deptno = dept.deptno) cnt from dept;
--在having子句中的子查询
--查询员工信息表,按部门编号进行分组,要求显示员工的部门编号,平均工资,查询条件是平均工资大于30号部门的最高工资。
select deptno,avg(sal)
from emp
group by deptno
having avg(sal)>(select max(sal)
from emp
where deptno = 30);
--查询并显示高于部门平均工资的雇员信息
select ename,job,sal
from emp,(select deptno,avg(sal) avgsal from emp group by deptno) dept
where emp.deptno = dept.deptno and sal>avgsal;
---子查询和主查询
SELECT select_list
FROM table
WHERE expr operator--主查询
(SELECT select_list
FROM table);--子查询
一个主查询可以有多个子查询
--显示职位和7521的职位相同并且工资大于7934这个员工工资的员工信息。
select *
from emp
where job = (select job from emp where empno = 7521)
and sal>(select sal from emp where empno = 7934);
一般先执行子查询,再执行主查询,但相关子查询例外
--查询员工表中小于平均工资的员工信息
select empno,ename,sal
from emp
where sal <(select avg(sal) from emp);
主查询和子查询可以不是同一张表
--查询部门名称是 ACCOUNTING的员工信息。
select *
from emp
where deptno = (select deptno from dept where dname='ACCOUNTING');
只返回一行数据的子查询语句·使用单行比较操作符
操作符 | 含义 |
---|---|
= | 等于 |
> | 大于 |
>= | 大于等于 |
< | 小于 |
<= | 小于等于 |
!= | 不等于 |
示例:
--显示与JAMES同部门的所有其他的员工姓名、工资、部门号。
select ename,sal,deptno
from emp
where deptno = (select deptno from emp where ename='JAMES')
and ename<>'JAMES';
--查询大于等于公司平均工资的员工的姓名、职位、工资。
select ename,job,sal
from emp
where sal >=(select avg(sal) from emp);
--查询部门名称不是’SAVE’S的员工。
select *
from emp
where deptno <> (select deptno from dept where dname = 'SAVES');
--非法使用单行子查询
select ename, job, sal
from emp
where sal = (select max(sal)
from emp
group by deptno);
运算符 | 含义 |
---|---|
in | 等于列表中的任何一个 |
not in | 不等于列表中的任何一个 |
ALL | 和子查询返回的所有值进行比较 |
ANY | ANY和子查询返回的任一值进行比较 |
在多行子查询中使用IN操作符
----查询工作地点在NEW YORK和CHICAGO的部门所对应的员工信息。
select *
from emp
where deptno in
(select deptno from dept where loc = 'NEW YORK' or loc='CHICAGO');
在多行子查询中使用not 操作符
----查询工作地点不在NEW YORK和CHICAGO的部门所对应的员工信息。
select *
from emp
where deptno not in
(select deptno from dept where loc = 'NEW YORK' or loc='CHICAGO');
在多行子查询中使用ALL操作符
--查询高于30号部门所有员工工资的员工名、工资和部门号。
select ename,sal,deptno
from emp
where sal>all(select sal from emp where deptno = 30);
--单行子查询
select ename,sal,deptno
from emp
where sal>(select max(sal) from emp where deptno = 30);
在多行子查询中使用ALL操作符
--any操作符
--查询高于10号部门任意一个员工工资的员工名、工资和部门号。
select ename,sal,deptno
from emp
where sal > any(select sal from emp where deptno = 10);
--单行子查询
select ename,sal,deptno
from emp
where sal > (select min(sal) from emp where deptno = 10);
1.不可以在 group by子句中使用子查询
--错误示范
select avg(sal) from emp group by (select deptno from emp);
在TOP-N分析问题中,须对子查询排序
--显示工资信息表中工资最高的前五名员工,n就是5,取出最前面的n条数据
--rownum
select rownum, empno,ename from empnew where rownum<=5;
--查询员工的工资
select sal from emp;
--错误的
select empno,ename,sal
from emp
where rownum<=5
order by sal desc;
--正确的
--在TOP-N分析问题中,须对子查询排序
select empno,ename,sal
from (select * from emp order by sal desc)
where rownum<=5;
单行子查询:如果子查询返回了一个空值,则主查询将不会查到任何结果。
多行子查询:
--空值问题
select *
from emp
where empno not in(select mgr from emp where mgr is not null);
DECLARE
/*
*声明部分:声明变量、常量、复杂数据类型、游标等
*/
BEGIN
/*
*执行部分:PL/SQL和SQL语句
*/
EXCEPTION
/*
*异常处理部分:处理运行错误
*/
END;--块结束标记
打印一句hello world!
BEGIN
dbms_output.put_line('hello world!');
END;
注意:当Command Window窗口使用中DBMS_OUTPUT包输出信息时,需要设置SQL PLUS环境serveroutput的值为ON
>SQL set serveroutput on
SQL>
SQL> BEGIN
2 dbms_output.put_line('hello world');
3 END;
4 /--代表执行上述程序
PL/SQL procedure successfully completed
SQL> set serveroutput on--开启DBMS_OUTPUT包
SQL> /
hello world
PL/SQL procedure successfully completed
注意:尽量不要变量名声明和表字段名一样
标识符 | 命名规则 | 例子 |
---|---|---|
程序变量 | v_name | v_sal |
程序常量 | c_name | c_pi |
游标常量 | name_curror | emp_curror |
异常标识 | e_name | e_integrity_error |
记录类型 | name_record | emp_record |
为提高代码的可读性,建议使用命令方法。
代码示例:
DECLARE
v_name VARCHAR2(10);--定义一个变量
v_sal NUMBER(7.2);
v_hiredate DATE;
c_tax_rate CONSTANT NUMBER(3.2) := 0.02;--定义一个常量并赋予初始值
v_tax_sal NUMBER(7.2);
v_valid BOOLEAN DEFAULT TRUE;--定义一个boolean类型的变量
BEGIN
SELECT ename, sal, hiredate
INTO v_name, v_sal, v_hiredate
FROM emp
WHERE empno = 7369;--将将7369的员工信息通过into一一对应传给相应类型的变量
v_tax_sal := v_sal * c_tax_rate;--计算所得税
dbms_output.put_line(v_name || '的工资是:' || v_sal || '雇佣日期是:' ||
v_hiredate || '所得税是:' || v_tax_sal);--打印输出变量中的内容
IF v_valid THEN
dbms_output.put_line('已核实');
END IF;
END;
--输出结果:
SMITH的工资是:800雇佣日期是:17-12月-80所得税是:0
已核实
--注释内容
/*注释内容*/
代码示例:
--定义变量来存储表中查询字段的结果
DECLARE
v_name VARCHAR2(10);
v_sal NUMBER(7, 2);
BEGIN
SELECT ename, sal INTO v_name, v_sal FROM emp WHERE empno = 7369;
dbms_output.put_line(v_name || '的工资是:' || v_sal);
END;
DECLARE
--定义引用型变量
v_name emp.ename%TYPE;--引用型变量的类型和emp表中的ename的字段的数据类型一样
v_sal emp.sal%TYPE;
BEGIN
--将7369的员工信息一一对应赋值给定义好的引用型变量
SELECT ename, sal INTO v_name, v_sal FROM emp WHERE empno = 7369;
dbms_output.put_line(v_name || '的工资是:' || v_sal);
END;
DECLARE
--定义一个记录型变量
emp_record emp%ROWTYPE;
BEGIN
--将7369的员工信息赋值给记录型变量emp_record
SELECT * INTO emp_record FROM emp WHERE empno = 7369;
--记录型变量分量来获取表中每个字段的内容进行打印输出
dbms_output.put_line(emp_record.ename || '的工资是:' || emp_record.sal);
END;
运算符 | 含义 |
---|---|
+ | 加号 |
- | 减号 |
* | 乘号 |
/ | 除号 |
** | 乘方 |
代码示例:
DECLARE
v_num1 NUMBER(3) := 10;
v_num2 NUMBER(3) := 2;
BEGIN
dbms_output.put_line(v_num1 + v_num2);--10+2
dbms_output.put_line(v_num1 - v_num2);--10-2
dbms_output.put_line(v_num1 * v_num2);--10*2
dbms_output.put_line(v_num1 / v_num2);--10/2
dbms_output.put_line(v_num1 ** v_num2);--10^2
END;
运算符 | 含义 |
---|---|
= | 等于 |
<>,!=,~=,^= | 不等于 |
< | 小于 |
> | 大于 |
<= | 小于等于 |
>= | 大于等于 |
代码示例:
DECLARE
v_num1 NUMBER(3) := &input_n1;--&input_n1是指通过键盘输入值给变量
v_num2 NUMBER(3) := &input_n2;
BEGIN
IF (v_num1 = v_num2) THEN
dbms_output.put_line('v_num1等于v_num2');
ELSIF (v_num1 < v_num2) THEN
dbms_output.put_line('v_num1小于v_num2');
ELSIF (v_num1 > v_num2) THEN
dbms_output.put_line('v_num1大于v_num2');
END IF;
IF (v_num1 <> v_num2) THEN
dbms_output.put_line('v_num1不等于v_num2');
END IF;
END;
运算符 | 含义 |
---|---|
IS NULL | 是空值 |
BETWEEN…AND | 介于两者之间 |
IN | 等与列表中的某个值 |
代码示例:
DECLARE
v_num NUMBER(3) := &input_n1;
BEGIN
IF (v_num BETWEEN 5 AND 10) THEN
dbms_output.put_line('v_num在5到10之间');
ELSE
dbms_output.put_line('v_num不在5到10之间');
END IF;
IF (v_num IN (8, 9.10, 11)) THEN
dbms_output.put_line('v_num在8,9.10,11中的一个值');
ELSE
dbms_output.put_line('v_num不在8,9.10,11中的一个值');
END IF;
IF (v_num IS NULL) THEN
dbms_output.put_line('v_num为空');
ELSE
dbms_output.put_line('v_num不为空');
END IF;
END;
运算符 | 含义 |
---|---|
AND | 逻辑与 |
OR | 逻辑或 |
NOT | 取反,如IS NULL,NOT IN |
DECLARE
v_flag1 BOOLEAN := &input_n1;
v_flag2 BOOLEAN := &input_n2;
BEGIN
IF (v_flag1 AND v_flag2) THEN
dbms_output.put_line('and两个条件都为ture,为true');
ELSE
dbms_output.put_line('and两个条件都不为ture,则false');
END IF;
IF (v_flag1 OR v_flag2) THEN
dbms_output.put_line('or两个条件都不为false,为true');
ELSE
dbms_output.put_line('or两个条件都为false,才为false');
END IF;
IF (NOT v_flag1) THEN
dbms_output.put_line('v_flag1取反为true');
END IF;
END;
--语法如下:
variable := expression/value;
--示例
v_var := 5*4;
v_vbr :=20;
字符及数字运算特点:
--语法格式:
IF condition THEN
statements;
END IF;
--操作示例:
需求:输入员工编号,判断员工的工资,显示工资小于3000的员工姓名及工资
DECLARE
v_name emp.ename%TYPE;
v_sal emp.sal%TYPE;
BEGIN
SELECT ename, sal INTO v_name, v_sal FROM emp WHERE empno = &input_empno;
IF v_sal < 3000 THEN
dbms_output.put_line(v_name || '的工资是:' || v_sal);
END IF;
END;
--语法格式:
IF condition THEN
statements;
ELSE
statements;
END IF;
--操作示例:
需求:输入员工编号,判断员工的工资,将工资小于3000的员工工资涨200,并显示涨工资员工姓名及工资,以及其他员工的姓名和工资
DECLARE
v_name emp.ename%TYPE;
v_sal emp.sal%TYPE;
v_empno emp.empno%TYPE := &input_empno;--当多次需要使用到员工编号,可以用一个变量来存储输入的员工编号
BEGIN
SELECT ename, sal INTO v_name, v_sal FROM emp WHERE empno = v_empno;
IF v_sal < 3000 THEN
dbms_output.put_line(v_name || '涨工资前工资是:' || v_sal);
v_sal := v_sal + 200; --涨工资
UPDATE emp01 SET sal = v_sal WHERE empno = v_empno;
COMMIT;--delete insert update 一定要COMMIT;
dbms_output.put_line(v_name || '涨工资后工资是:' || v_sal);
ELSE
dbms_output.put_line(v_name || '的工资是:' || v_sal);
END IF;
END;
--语法格式:
IF condition THEN
statements;
ELSIF condition THEN
statements;
ELSE
statements;
END IF;
--操作示例:
需求:输入员工编号,判断员工的工资,将工资小于3000,显示低等收入,工资小于6000,显示中等收入,其他显示高等收入
SELECT * FROM emp01;
DECLARE
v_name emp.ename%TYPE;
v_sal emp.sal%TYPE;
v_empno emp.empno%TYPE := &input_empno;
BEGIN
SELECT ename, sal INTO v_name, v_sal FROM emp WHERE empno = v_empno;
IF v_sal < 3000 THEN
dbms_output.put_line(v_name || '的工资是:' || v_sal || '属于低等收入');
ELSIF v_sal < 5000 THEN
dbms_output.put_line(v_name || '的工资是:' || v_sal || '属于中等收入');
ELSE
dbms_output.put_line(v_name || '的工资是:' || v_sal || '属于高等收入');
END IF;
END;
使用CASE语句处理多重条件分支由两种方法
--语法格式:
CASE 要判断的字段或表达式1
WHEN 常量1 THEN 显示的值1或者语句1;
WHEN 常量2 THEN 显示的值2或者语句2;
WHEN 常量3 THEN 显示的值3或者语句3;
....
ELSE 显示的值或者语句n;
END CASE;
--操作示例:
需求:输入成绩等级,判断输入哪个层次,并打印输出。
DECLARE
v_grade CHAR(1) := UPPER('&n');
BEGIN
CASE v_grade
WHEN 'A' THEN
dbms_output.put_line('优秀');
WHEN 'B' THEN
dbms_output.put_line('中等');
WHEN 'C' THEN
dbms_output.put_line('一般');
WHEN 'D' THEN
dbms_output.put_line('差劲');
ELSE
dbms_output.put_line('输入有误!');
END CASE;
END;
--语法格式:
CASE
WHEN 条件1 THEN 显示的值1或者语句1;
WHEN 条件2 THEN 显示的值1或者语句1;
WHEN 条件3 THEN 显示的值1或者语句1;
WHEN 条件4 THEN 显示的值1或者语句1;
ELSE 显示的值或者语句n;
END CASE;
--操作示例:
需求:输入员工编号,获取员工工资,判断工资,如果工资小于1000,补助200;如果工资小于2000,补助100,如果工资小于5000,补助50
--提示:为方便测试,添加数据
CREATE TABLE emp01 as select * from emp;
SELECT * FROM emp01;
INSERT INTO emp01 VALUES(1111,'qzp','MANAGER',NULL,SYSDATE,10000,0,50);
COMMIT;
--开始操作:
DECLARE
v_name emp.ename%TYPE;
v_sal emp.sal%TYPE;
v_empno emp.empno%TYPE := &input_empno;
BEGIN
SELECT ename, sal INTO v_name, v_sal FROM emp01 WHERE empno = v_empno;
CASE
WHEN v_sal <= 1000 THEN
UPDATE emp01 SET comm = NVL(comm, 0) + 200 WHERE empno = v_empno;
dbms_output.put_line('更新成功!');
WHEN v_sal <= 2000 THEN
UPDATE emp01 SET comm = NVL(comm, 0) + 100 WHERE empno = v_empno;
dbms_output.put_line('更新成功!');
WHEN v_sal <= 5000 THEN
UPDATE emp01 SET comm = NVL(comm, 0) + 50 WHERE empno = v_empno;
dbms_output.put_line('更新成功!');
ELSE
dbms_output.put_line('抱歉,你不在我们补助范围之内!');
END CASE;
COMMIT;
END;
--语法格式:
LOOP
statement1;
....
END LOOP;
--操作示例:
需求:打印数字1到10
DECLARE
v_cnt INT := 1;
BEGIN
LOOP
dbms_output.put_line(v_cnt);
EXIT WHEN v_cnt = 10;--跳出循环语句,避免死循环
v_cnt := v_cnt + 1;
END LOOP;
END;
--语法格式:
WHILE condition LOOP
statement1;
statement2;
....
END LOOP;
--操作示例:
需求:打印数字1到10
DECLARE
v_cnt INT := 1;
BEGIN
WHILE v_cnt <= 10 LOOP
dbms_output.put_line(v_cnt);
v_cnt := v_cnt + 1;
END LOOP;
END;
--语法格式:
FOR counter in [REVERSE]lower_bound .. upper_bound LOOP
statements1;
statements2;
....
END LOOP;
注意:for默认是自增输出,REVERSE:自减输出
--操作示例:
需求:打印数字1到10
BEGIN
FOR i IN REVERSE 1..10 LOOP
dbms_output.put_line(i);
END LOOP;
END;
DECLARE
v_result INT;
BEGIN
<> --标记外层循环
FOR i IN 1 .. 5 LOOP
<> --标记内层循环
FOR j IN 1 .. 5 LOOP
v_result := i;
EXIT outter WHEN i = 4;--满足该条件直接退出外循环
END LOOP inter;
dbms_output.put_line('内' || v_result);
END LOOP outter;
dbms_output.put_line('外' || v_result);
END;
EXIT:用于直接退出当前循环
EXIT WHEN:用于满足特定条件的情况下退出当前循环
--exit
DECLARE
v_cnt INT := 1;
BEGIN
LOOP
dbms_output.put_line(v_cnt);
IF v_cnt = 10 THEN
EXIT;
END IF;
v_cnt := v_cnt + 1;
END LOOP;
END;
--exit when
DECLARE
v_cnt INT := 1;
BEGIN
LOOP
dbms_output.put_line(v_cnt);
EXIT WHEN v_cnt = 10;
v_cnt := v_cnt + 1;
END LOOP;
END;
CONTINUE:用于直接结束当前循环并继续下一组循环
CONTINUE WHEN:用于在满足特定条件时结束当前循环语句并继续下一组循环语句
--continue
DECLARE
v_cnt INT := 0;
BEGIN
LOOP
v_cnt := v_cnt + 1;
IF v_cnt = 5 THEN
CONTINUE;--满足条件,退出当前循环,进入下一轮循环
END IF;
dbms_output.put_line(v_cnt);
IF v_cnt = 10 THEN
EXIT;
END IF;
END LOOP;
END;
--continue when
DECLARE
v_cnt INT := 0;
BEGIN
LOOP
v_cnt := v_cnt + 1;
CONTINUE WHEN v_cnt = 5;--满足条件,退出当前循环,进入下一轮循环
dbms_output.put_line(v_cnt);
EXIT WHEN v_cnt = 10;
END LOOP;
END;
--语法格式:
GOTO label_name;
注意:当使用GOTO跳转到特定标号时,标号后至少要包含一条执行语句
--需求:打印数字1到10,用LOOP语句处理
DECLARE
v_cnt INT := 1;
BEGIN
LOOP
dbms_output.put_line(v_cnt);
IF v_cnt = 10 THEN
EXIT;
END IF;
v_cnt := v_cnt + 1;
END LOOP;
END;
--GOTO(不推荐使用)
DECLARE
v_cnt INT := 1;
BEGIN
LOOP
dbms_output.put_line(v_cnt);
IF v_cnt = 10 THEN
GOTO end_loop;
END IF;
v_cnt := v_cnt + 1;
END LOOP;
<>
dbms_output.put_line('循环结束');--必须存在一条语句
END;
--需求:根据输入的员工编号,判断员工的工资,如果工资小于3000,将员工的补助加工资的2%,并打印输出某员工的奖金更新了
DECLARE
v_name emp01.ename%TYPE;
v_sal emp01.sal%TYPE;
v_comm emp01.comm%TYPE;
v_empno emp01.empno%TYPE := &input_empno;
BEGIN
SELECT ename, sal, comm
INTO v_name, v_sal, v_comm
FROM emp01
WHERE empno = v_empno;
IF v_sal < 3000 THEN
dbms_output.put_line(v_name || '涨工资前工资是:' || (v_sal + NVL(v_comm, 0)));
v_comm := NVL(v_comm, 0) + v_sal * 0.2;
UPDATE emp01 SET comm = v_comm WHERE empno = v_empno;
COMMIT;
dbms_output.put_line(v_name || '涨工资后工资是:' || (v_sal + NVL(v_comm, 0)));
ELSE
NULL;
END IF;
END;
1、DDL语句、DCL语句、非查询的DML语句、单行查询的SELECT语句,这类可以使用EXECUTE IMMEDIATE语句执行。
2、多行查询的SELECT语句可以使用游标来实现。
3、通过DBMS_SQL程序包实现。
语法格式:
EXECUTE IMMEDIATE dynamic_sql_string
[into define_variable_list]
[using bind_argument_list];
DECLARE
TABLENAME VARCHAR2(20);
FIELD1 VARCHAR2(20);
DATATYPE1 VARCHAR2(20);
FIELD2 VARCHAR2(20);
DATATYPE2 VARCHAR2(20);
STR_SQL VARCHAR2(500);
BEGIN
TABLENAME := 't1';
FIELD1 := 'id';
DATATYPE1 := 'number';
FIELD2 := 'name';
DATATYPE2 := 'varchar2(20)';
STR_SQL := 'create table ' || TABLENAME || '(' || FIELD1 || ' ' ||
DATATYPE1 || ',' || FIELD2 || ' ' || DATATYPE2 || ')';
EXECUTE IMMEDIATE STR_SQL;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLCODE || ' ' || SQLERRM);
DBMS_OUTPUT.PUT_LINE('操作失败!!!');
END;
--动态处理费查询的DML语句:向刚才创建的表中插入数据
DECLARE
v_id NUMBER; --输入序号;
v_name VARCHAR(20); --输入姓名;
str_sql VARCHAR2(500); --保存拼接的SQL语句
BEGIN
v_id := &vid;
v_name := '&name';
str_sql := 'insert into t1 values(:1,:2)'; --使用占位符代表变量
EXECUTE IMMEDIATE str_sql
USING v_id,v_name; --使用变量替换SQL中的占位符,v_id替换:1,v_n
--处理单行查询的SELECT举例,查询表中的数据有多少行
DECLARE
v_count NUMBER;
str_sql VARCHAR2(500);
BEGIN
str_sql := 'select count(*) from t1';
EXECUTE IMMEDIATE str_sql INTO v_count; --将查询的结果存放到变量v_count中。
DBMS_OUTPUT.put_line(v_count);
END;
知识链接:https://www.cnblogs.com/zhengcheng/p/4207376.html
显式游标处理的四个步骤:
CURSOR cursor_name[(parameter_name datatype)] is select_statement;
OPEN cursor_name;
FETCH cursor_name INTO variable1[,variable2,...];--注意:每次只能提取一行数据
CLOSE cursor_name;
操作示例:
--需求:查询所有员工的员工编号、姓名和职位的信息
DECLARE
--定义游标
CURSOR emp_cursor IS
SELECT empno, ename, job FROM emp;
--定义引用型变量
v_empno emp.empno%TYPE;
v_ename emp.ename%TYPE;
v_job emp.job%TYPE;
BEGIN
--打开游标,执行查询
OPEN emp_cursor;
--提取数据
LOOP
FETCH emp_cursor
INTO v_empno, v_ename, v_job;
dbms_output.put_line('员工编号是:' || rpad(v_empno, 6) || '员工姓名:' ||
rpad(v_ename, 10) || '员工职位是:' || v_job);
--判断游标是否存在数据来退出循环
IF emp_cursor%NOTFOUND THEN
EXIT;
END IF;
END LOOP;
--关闭游标
CLOSE emp_cursor;
END;
显式游标的属性
%FOUND
%NOTFOUND
%ISOPEN
%ROWCOUNT
--语法如下:
FOR record_name IN cursor_name LOOP
statements;
END LOOP;
--操作示例:
--需求:查询所有员工的员工编号、姓名和职位的信息
--方式一:使用游标for循环
DECLARE
CURSOR emp_cursor IS
SELECT empno, ename, job FROM emp;
BEGIN
FOR emp_record IN emp_cursor LOOP
dbms_output.put_line('员工编号是:' || rpad(emp_record.empno, 6) || '员工姓名:' ||
rpad(emp_record.ename, 10) || '员工职位是:' ||
emp_record.job);
END LOOP;
END;
--方式二:使用子查询
BEGIN
FOR emp_record IN (SELECT empno, ename, job FROM emp) LOOP
dbms_output.put_line('员工编号是:' || rpad(emp_record.empno, 6) || '员工姓名:' ||
rpad(emp_record.ename, 10) || '员工职位是:' ||
emp_record.job);
END LOOP;
END;
--定义参数游标的语法格式:
CURSOR cursor_name(parameter_name datatype) IS select_statement;
--打开参数游标的语法格式:
OPEN cursor_name(parameter_value);
--操作示例:
DECLARE
CURSOR emp_cursor(dno NUMBER) IS
SELECT empno, ename, job FROM emp WHERE deptno = dno;
BEGIN
FOR emp_record IN emp_cursor(&input_deptno) LOOP
dbms_output.put_line('员工编号是:' || rpad(emp_record.empno, 6) || '员工姓名:' ||
rpad(emp_record.ename, 10) || '员工职位是:' ||
emp_record.job);
END LOOP;
END;
显式游标是用户自定义的显式创建的游标,主要是用于对查询语句的处理
隐式游标是由系统隐含创建的游标,主要用于对非查询语句,如修改,删除等操作。则有Oracle系统自动地为这些操作设置游标并创建其工作区,对于隐式游标地操作,如定义、打开、取值及关闭操作,都有Oracle系统自动完成,无需用户进行处理,名字为SQL,这是由Oracle系统定义的。
当系统使用一个隐式游标时,可以通过隐式游标的属性来了解操作的状态和结果,进而控制程序的流程
注意:通过SQL游标名总是只能访问前一个DML操作或单行SELECT操作的游标属性
隐式游标的属性:
--需求:根据用户输入的员工号,更新指定员工的工资,比如工资涨100
BEGIN
UPDATE emp01 SET sal = sal + 100 WHERE empno = &input_deptno;
IF SQL%FOUND THEN
dbms_output.put_line('更新成功!!!');
COMMIT;
ELSE
dbms_output.put_line('更新失败!!!');
ROLLBACK;
END IF;
END;
CURSOR cursor_name IS select_statement FOR UPDATE [OF column_reference][NOWALT];
UPDATE table_name SET colunm=.. WHERE CURRENT OF cursor_name;
DELETE FROM table_name WHERE CURRENT OF cursor_name;
FOR UPDATE子句会将游标提取出来的数据进行行级锁定,这样在会话更新期间,其他用户的会话就不能对当前游标中的数据行进行更新操作。
--方式一:
DECLARE
CURSOR emp01_cursor IS
SELECT empno, ename, job, sal FROM emp01;
BEGIN
FOR emp_record IN emp01_cursor LOOP
dbms_output.put_line(emp_record.ename || '的工资是:' || emp_record.sal);
IF emp_record.job = 'PRESIDENT' THEN
UPDATE emp01 SET sal = sal + 1000 WHERE empno = emp_record.empno;
ELSIF emp_record.job = 'MANAGER' THEN
UPDATE emp01 SET sal = sal + 500 WHERE empno = emp_record.empno;
ELSIF emp_record.job = 'CLERK' THEN
UPDATE emp01 SET sal = sal + 300 WHERE empno = emp_record.empno;
ELSE
UPDATE emp01 SET sal = sal + 200 WHERE empno = emp_record.empno;
END IF;
END LOOP;
COMMIT;
END;
--方式二:
DECLARE
CURSOR emp01_cursor IS
SELECT empno, ename, job, sal FROM emp01 FOR UPDATE;
BEGIN
FOR emp_record IN emp01_cursor LOOP
dbms_output.put_line(emp_record.ename || '的工资是:' || emp_record.sal);
IF emp_record.job = 'PRESIDENT' THEN
UPDATE emp01 SET sal = sal + 1000 WHERE CURRENT OF emp01_cursor;
ELSIF emp_record.job = 'MANAGER' THEN
UPDATE emp01 SET sal = sal + 500 WHERE CURRENT OF emp01_cursor;
ELSIF emp_record.job = 'CLERK' THEN
UPDATE emp01 SET sal = sal + 300 WHERE CURRENT OF emp01_cursor;
ELSE
UPDATE emp01 SET sal = sal + 200 WHERE CURRENT OF emp01_cursor;
END IF;
END LOOP;
COMMIT;
END;
NOWALT
DECLARE
CURSOR emp01_cursor IS
SELECT empno, ename, job, sal FROM emp01 FOR UPDATE NOWAIT;
BEGIN
FOR emp_record IN emp01_cursor LOOP
dbms_output.put_line(emp_record.ename || '的工资是:' || emp_record.sal);
IF emp_record.job = 'PRESIDENT' THEN
UPDATE emp01 SET sal = sal + 1000 WHERE CURRENT OF emp01_cursor;
ELSIF emp_record.job = 'MANAGER' THEN
UPDATE emp01 SET sal = sal + 500 WHERE CURRENT OF emp01_cursor;
ELSIF emp_record.job = 'CLERK' THEN
UPDATE emp01 SET sal = sal + 300 WHERE CURRENT OF emp01_cursor;
ELSE
UPDATE emp01 SET sal = sal + 200 WHERE CURRENT OF emp01_cursor;
END IF;
END LOOP;
COMMIT;
END;
OF column_reference
--需求:输入部门号,显示该部门的部门名称及员工的姓名,并删除该部门下的这些员工
DECLARE
CURSOR emp01_cursor IS
SELECT d.dname dname, e.ename ename
FROM emp01 e
JOIN dept d
ON e.deptno = d.deptno
WHERE e.deptno = &input_deptno
FOR UPDATE OF e.deptno;
BEGIN
FOR emp01_record IN emp01_cursor LOOP
dbms_output.put_line('部门名称:' || emp01_record.dname || '员工姓名是:' ||
emp01_record.ename);
DELETE FROM emp01 WHERE CURRENT OF emp01_cursor;
END LOOP;
COMMIT;
END;
--操作示例:
需求:输入员工编号,判断员工的工资,显示工资小于3000的员工姓名及工资
DECLARE
v_name emp.ename%TYPE;
v_sal emp.sal%TYPE;
BEGIN
SELECT ename, sal INTO v_name, v_sal FROM emp WHERE empno = &input_empno;
IF v_sal < 3000 THEN
dbms_output.put_line(v_name || '的工资是:' || v_sal);
END IF;
END;
上述代码有一个很大的问题,如果查找的员工不存在呢,程序会报错,如何解决?
ORA-01403: 未找到任何数据
ORA-06512: 在 line 5
--语法格式:
EXCEPTION
WHEN first_exception THEN
statement1;
....
WHEN second_exception THEN
statement1;
....
WHEN OTHERS THEN
statement1;
....
操作示例:解决上述报的异常问题
DECLARE
v_name emp.ename%TYPE;
v_sal emp.sal%TYPE;
BEGIN
SELECT ename, sal INTO v_name, v_sal FROM emp WHERE empno = &input_empno;
IF v_sal < 3000 THEN
dbms_output.put_line(v_name || '的工资是:' || v_sal);
END IF;
EXCEPTION
WHEN no_data_found THEN
dbms_output.put_line('员工编号有误');
END;
异常码 | 异常名称 | 描述 |
---|---|---|
ORA-00001 | DUP_VAL_ON_INDEX | 试图向唯一索引列插入重复值,破环唯一性限制 |
ORA-00051 | TIMEOUT_ON_RESOURCE | 在等待资源时发生超时 |
ORA-01001 | INVALID_CURSOR | 试图使用一个无效地游标 |
ORA-01012 | NOT_LOGGED_ON | 没有连接到ORACLE |
ORA-01017 | LOGIN_DENIED | 无效地用户名和口令 |
ORA-01403 | NO_DATA_FOUND | SELECT INTO 语句中没有返回任何记录。 |
ORA-01410 | SYS_INVALID_ROWID | 从字符串向ROWID转换发生错误 |
ORA-01422 | TOO_MANY_ROWS | SELECT INTO 语句中返回多于 1 条记录。 |
ORA-01722 | INVALID_NUMBER | 试图将字符串转换为数字,转换失败 |
ORA-06511 | CURSOR_ALREADY_OPEN | 试图打开一个已经打开的游标 |
ORA-01476 | ZERO_DIVIDE | 数字值除零时触发异常 |
ORA-06500 | STORAGE_ERROR | 内存不足引发地内部错误 |
ORA-06501 | PROGRAM_ERROR | 存在PL/SQL内部问题 |
ORA-06502 | VALUE_ERROR | 转换或截断错误 |
ORA-06504 | ROWTYPE_MISMATCH | 宿主游标变量与PL/SQL游标变量地返回类型不兼容 |
ORA-06530 | ACCESS_INTO_NULL | 未定义对象 |
ORA-06531 | COLLECTION_IS_NULL | 集合元素未初始化 |
ORA-06532 | SUBSCRITP_OUTSIDE_LIMIT | 使用嵌套表或VARRAY时,将下标指定为负数 |
ORA-06533 | SUBSCRIPT_BEYOND_COUNT | 元素下标超过嵌套表或VARRAY的最大值 |
ORA-06592 | CASE_NOT_FOUND | CASE中若未包含相应的WHEN,并且没有设置 |
ORA-30625 | SELF_IS_NULL | 使用对象类型时,在null对象上调用对象方法 |
--希求:根据输入的工资,查询员工的姓名,并输出员工的姓名及工资
DECLARE
v_name emp.ename%TYPE;
v_sal emp.sal%TYPE := &salary;
BEGIN
SELECT ename, sal INTO v_name, v_sal FROM emp WHERE sal = v_sal;
dbms_output.put_line(v_name || '的工资是:' || v_sal);
EXCEPTION
WHEN no_data_found THEN
dbms_output.put_line('没有该工资的员工');
WHEN too_many_rows THEN
dbms_output.put_line('多个员工具有该工资');
WHEN OTHERS THEN
dbms_output.put_line('其他异常');
END;
非预定义异常的处理包括3步:
1.在PL/SQL块定义部分定义异常情况:
<异常情况> EXCEPTION;
2.将其定义好的异常情况与标准的ORACLE错误联系起来,使用PRAGMA EXCEPTION_INIT语句:
PRAGMA EXCEPTION_INIT(<异常情况>,<错误代码>);
3.在PL/SQL块的异常情况处理部分对异常情况做出相应的处理
--删除dept表中指定部门的信息
BEGIN
DELETE FROM dept WHERE deptno = &input_deptno;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('错误代码号:' || SQLCODE || '错误信息:' || SQLERRM);
END;
错误代码号:-2292错误信息:ORA-02292: 违反完整约束条件 (SCOTT.FK_DEPTNO) - 已找到子记录
--解决方法
DECLARE
--1.定义非预定义异常的标识符
e_fk EXCEPTION;
--2.把oracle错误与异常建立关联
--错误代码号:-2292错误信息:ORA-02292: 违反完整约束条件 (SCOTT.FK_DEPTNO) - 已找到子记录
PRAGMA EXCEPTION_INIT(e_fk, -2292);
BEGIN
DELETE FROM dept WHERE deptno = &input_deptno;
COMMIT;
EXCEPTION
WHEN e_fk THEN
--3.捕捉并处理异常
dbms_output.put_line('此部门下有员工,不能删除');
WHEN OTHERS THEN
dbms_output.put_line('错误代码号:' || SQLCODE || '错误信息:' || SQLERRM);
END;
如果你想在某个特定事件发生时向应用程序的用户发出一些警告信息,而事件本身不会抛出Oracle内部异常,这个异常是属于应用程序的特定异常,那么就需要自定义异常。用户自定义的异常错误是是通过显式使用RALSE语句来触发,当引发一个异常错误时,控制就转向到EXCEPTION块异常错误部分,执行错误处理代码。
自定义异常的处理步骤:
1.在PL/SQL块的声明部分定义异常情况:
<异常情况> EXCEPTION;
2.使用RALSE触发
RAISE<异常情况>
3.在PL/SQL块的异常情况处理部分对异常情况做出相应的处理
--自定义异常
DECLARE
v_empno emp01.empno%TYPE := &input_empno;
e_no_result EXCEPTION;
BEGIN
UPDATE emp01 SET sal = sal + 100 WHERE empno = v_empno;
IF SQL%NOTFOUND THEN
RAISE e_no_result;
ELSE
COMMIT;
END IF;
EXCEPTION
WHEN e_no_result THEN
dbms_output.put_line('数据更新失败!');
WHEN OTHERS THEN
dbms_output.put_line('其他异常');
END;
DECLARE
V_EMPNO EMP.EMPNO%TYPE := &INPUT_EMPNO;
V_ENAME EMP.ENAME%TYPE := '&input_ename';
V_DEPTNO EMP.DEPTNO%TYPE := &DEPTNO;
BEGIN
INSERT INTO EMP
(EMPNO, ENAME, DEPTNO)
VALUES
(V_EMPNO, V_ENAME, V_DEPTNO);
IF SQL%FOUND THEN
DBMS_OUTPUT.PUT_LINE('数据插入成功');
COMMIT;
END IF;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('错误号:' || SQLCODE || '错误信息:' || SQLERRM);
END;
错误号:-2291错误信息:ORA-02291: 违反完整约束条件 (SCOTT.FK_DEPTNO) - 未找到父项关键字
--语法格式
raise_application_error(error_number,message);
error_number:用于定义错误号(-2000~-20999)
message:用于指定错误信息,长度不能超过2048个字节
操作示例:
CREATE OR REPLACE PROCEDURE CHANGE_SAL(ENO NUMBER, SALARY NUMBER) IS
BEGIN
UPDATE EMP02 SET SAL = SALARY WHERE EMPNO = ENO;
IF SQL%NOTFOUND THEN
RAISE_APPLICATION_ERROR(-20002, '该员工不存在!!!');
ELSE
DBMS_OUTPUT.PUT_LINE('更新成功!!!');
COMMIT;
END IF;
END;
ORA-20002: 该员工不存在!!!
ORA-06512: 在 "SCOTT.CHANGE_SAL", line 5
ORA-06512: 在 line 2
相同点:
不同点:
需求:用存储过程或存储函数实现输出"Hello Everyone!"
--创建存储过程
CREATE OR REPLACE PROCEDURE FIRST_PROC IS
BEGIN
DBMS_OUTPUT.PUT_LINE('我是过程');
DBMS_OUTPUT.PUT_LINE('Hello world');
END;
--存储过程的调用
BEGIN
FIRST_PROC;
END;
--创建存储函数
CREATE OR REPLACE FUNCTION FIRST_FUNCTION RETURN VARCHAR2 IS
BEGIN
DBMS_OUTPUT.PUT_LINE('我是函数');
RETURN 'Hello world';
END;
--存储函数的调用
BEGIN
DBMS_OUTPUT.PUT_LINE(FIRST_FUNCTION);
END;
--语法格式:
CREATE [OR REPLACE] PROCEDURE Procedure_name [(argment1 [{IN | OUT | IN OUT}] type,argment2 [{IN | OUT | IN OUT}] type,....)]
{IS | AS}
--声明部分:声明变量、常量、复杂数据类型、游标等
BEGIN
--执行部分
EXCEPTION
--可选的异常错误处理部分
END;
CREATE OR REPLACE PROCEDURE SP_TEST_IN(P_V IN NUMBER) IS
V_SUM NUMBER;
BEGIN
-- P_V := 100;--in参数不能赋值,只能使用
DBMS_OUTPUT.PUT_LINE(P_V);
V_SUM := P_V + 100;
DBMS_OUTPUT.PUT_LINE(v_sum);
END;
---调用
BEGIN
sp_test_in(200);
END;
注意:输入参数,不允许直接给它赋值;可以在程序中使用
CREATE OR REPLACE PROCEDURE SP_TEST_OUT(P_V OUT NUMBER) IS
BEGIN
DBMS_OUTPUT.PUT_LINE(P_V);--out输出类型,
P_V := 100;
DBMS_OUTPUT.PUT_LINE(P_V);
END;
--调用,out不能直接使用begin end调用;
DECLARE
V_NUM NUMBER := 50;
BEGIN
SP_TEST_OUT(V_NUM);
END;
注意:out只能存储赋值之后的数据,入参的数据无法传递,且无法打印
CREATE OR REPLACE PROCEDURE SP_TEST_IN_OUT(P_V IN OUT NUMBER) IS
BEGIN
DBMS_OUTPUT.PUT_LINE(P_V);
P_V := 100;
DBMS_OUTPUT.PUT_LINE(P_V);
END;
--调用,out不能直接使用begin end调用;
DECLARE
V_NUM NUMBER := 50;
BEGIN
SP_TEST_IN_OUT(V_NUM);
END;
注意:in OUT 参数变量既可以传参,又可以被赋值,被修改
--语法格式:
CREATE [OR REPLACE] FUNCTION function_name [(argment1 [{IN | OUT | IN OUT}] type,argment2 [{IN | OUT | IN OUT}] type,....)]
RETURN return_type
{IS | AS}
--声明部分:声明变量、常量、复杂数据类型、游标等
BEGIN
--执行部分
EXCEPTION
--可选的异常错误处理部分
END;
--根据部门编号返回该部门的总工资
CREATE OR REPLACE FUNCTION ft_first_in(v_deptno IN NUMBER) RETURN NUMBER IS
v_sumsal NUMBER;
BEGIN
SELECT SUM(sal) INTO v_sumsal FROM emp WHERE deptno = v_deptno;
RETURN v_sumsal;
EXCEPTION
WHEN no_data_found THEN
dbms_output.put_line('未找到该部门');
WHEN OTHERS THEN
dbms_output.put_line(SQLERRM);
END;
--调用
BEGIN
dbms_output.put_line(ft_first_in(10));
END;
--根据员工编号输出员工的姓名和员工的工资,并返回员工的年收入
CREATE OR REPLACE FUNCTION ft_second_out(v_empno IN emp.empno%TYPE,
v_ename OUT emp.ename%TYPE,
v_sal OUT emp.sal%TYPE)
RETURN NUMBER IS
v_salsum NUMBER;
BEGIN
SELECT ename, sal, (sal + NVL(comm, 0)) * 12
INTO v_ename, v_sal, v_salsum
FROM emp
WHERE empno = v_empno;
RETURN v_salsum;
EXCEPTION
WHEN no_data_found THEN
dbms_output.put_line('没有此员工');
WHEN OTHERS THEN
dbms_output.put_line(SQLERRM);
END;
--------调用
DECLARE
v_ename emp.ename%TYPE;
v_sal emp.sal%TYPE;
BEGIN
dbms_output.put_line(ft_second_out(7369,v_ename,v_sal));
END;
--求两个数的平方和,并输出两个数的平方
CREATE OR REPLACE FUNCTION ft_thread_in_out(n1 IN OUT NUMBER,
n2 IN OUT NUMBER)
RETURN NUMBER IS
BEGIN
n1 := n1 * n1;
n2 := n2 * n2;
RETURN n1 + n2;
END;
--调用
DECLARE
v_n1 NUMBER :=&n1;
v_n2 NUMBER :=&n2;
BEGIN
dbms_output.put_line(ft_thread_in_out(v_n1,v_n2));
END;
方法一:Oracle使用EXECUTE语句来实现对存储过程的调用:
EXEC[UTE] Procedure_name(parameter1,parameter2...)
方法二:在PL/SQL代码中直接调用
BEGIN
Procedure_name(parameter1,parameter2...);
END;
--存储过程
DROP PROCEDURE [user.]Procedure_name;
--存储函数
DROP FUNCTION [user.]Function_name;
注意点:
--根据部门编号返回该部门的总工资
--根据部门编号返回该部门的总工资
CREATE OR REPLACE FUNCTION ft_first_in(v_deptno IN NUMBER DEFAULT 10,v_t IN NUMBER) --使用默认值
RETURN NUMBER IS
v_sumsal NUMBER;
BEGIN
SELECT SUM(sal) INTO v_sumsal FROM emp WHERE deptno = v_deptno;
dbms_output.put_line('测试:'||v_t);
RETURN v_sumsal;
EXCEPTION
WHEN no_data_found THEN
dbms_output.put_line('未找到该部门');
WHEN OTHERS THEN
dbms_output.put_line(SQLERRM);
END;
--调用
DECLARE
v_sum NUMBER;
BEGIN
v_sum := ft_first_in(v_t => 2);--当参数使用默认值,则用变量名=>来给指定变量赋值
dbms_output.put_line(v_sum);
END;
一个包由两个分开的部分组成:
--创建包的规范
CREATE [OR REPLACE] PACKAGE package_name
[IS|AS]
--定义公用常量、变量、游标、过程和函数等
END [package_name];
--创建包体
CREATE [OR REPLACE] PACKAGE BODY package_name
[IS|AS]
--定义私有常量、变量、游标、过程和函数等
--实现公共过程和函数
END first_package;
--包的调用
--对包内共有元素(公用组件)的调用格式为:
包名.元素名称(组件名称)
--包的删除
DROP PACKAGE [BODY] [user.]package_name;
操作示例:
--创建包的规范
CREATE OR REPLACE PACKAGE first_package IS
v_no emp.deptno%TYPE := 10;
--存储过程
PROCEDURE query_emp(v_deptno IN NUMBER DEFAULT v_no,
v_avgsal OUT NUMBER,
v_cnt OUT NUMBER);
END first_package;
--创建包体
CREATE OR REPLACE PACKAGE BODY first_package IS
--存储过程
PROCEDURE query_emp(v_deptno IN NUMBER DEFAULT v_no,
v_avgsal OUT NUMBER,
v_cnt OUT NUMBER) IS
BEGIN
SELECT AVG(sal), COUNT(*)
INTO v_avgsal, v_cnt
FROM emp
WHERE deptno = v_deptno;
EXCEPTION
WHEN no_data_found THEN
dbms_output.put_line('未找到该部门');
WHEN OTHERS THEN
dbms_output.put_line(SQLERRM);
END;
END first_package;
--调用
DECLARE
v_avgsal NUMBER;
v_cnt NUMBER;
v_deptno emp.deptno%TYPE := &input_deptno;
BEGIN
first_package.query_emp(v_deptno, v_avgsal, v_cnt);
dbms_output.put_line('平均工资' || v_avgsal);
dbms_output.put_line('总人数' || v_cnt);
END;
--创建序列的语法格式:
CREATE SEQUENCE sequence
[START WITH n] --从哪个值开始(初始值)
[INCREMENT BY n]--每次增长的数值(步长)
[{MAXVALUE n | NOMAXVALUE}]--默认是NOMAXVALUE
[{MINVALUE n | NOMINVALUE}]--默认是NOMINVALUE
[{CYCLE | NOCYCLE}] -- 是否需要循环,默认是NOCYCLE
[{CACHE n | NOCACHE}]--是否缓存,默认是NOCACHE
--使用序列
--当使用序列时,必须通过伪列NEXTVAL和CURRVAL来引用序列
--NEXTVAL:用于引用返回下一个序列值
deptno_sequence.nextval
--CURRVAL:用于引用返回当前序列值
deptno_sequence.currval
--查询序列
--查询数据字典视图 USER_SEQUENCES 获取序列定义信息
SELECT * from user_sequences;
--修改序列命令
ALTER SEQUENCE sequence_name
[INCREMENT BY n]--每次增长的数值(步长)
[{MAXVALUE n | NOMAXVALUE}]--默认是NOMAXVALUE
[{MINVALUE n | NOMINVALUE}]--默认是NOMINVALUE
[{CYCLE | NOCYCLE}] -- 是否需要循环,默认是NOCYCLE
[{CACHE n | NOCACHE}]--是否缓存,默认是NOCACHE
注意:序列的初始值不能修改!!!
注意事项:指定cache值,可以提高访问效率,但序列在如下情况会出现序列缺口
操作示例:
--创建序列
CREATE SEQUENCE deptno_sequence
START WITH 50
INCREMENT BY 10
MAXVALUE 70
CACHE 3;
CREATE TABLE dept1 AS SELECT * FROM dept;
SELECT * FROM dept1;
--利用序列来插入值
INSERT INTO dept1 VALUES(deptno_sequence.nextval,'qzp','beijing');
--查看当前的序列值
SELECT deptno_sequence.currval FROM dual;
注意:schema指方案名
--创建公共同义词:
CREATE PUBLIC SYNONYM synonym FOR [schema.]object;
--创建私有同义词:
CREATE SYNONYM synonym FOR [schema.]object;
--给权限
GRANT CREATE PUBLIC SYNONYM TO scott;
GRANT CREATE SYNONYM TO scott;
--创建公共的同义词
CREATE PUBLIC SYNONYM DN FOR scott.emp;
--当前用户下使用公共的同义词
SELECT * FROM DN;
--非当前用户下,对于公共的同义词可以直接使用
SELECT * FROM DN;
--创建私有的同义词
CREATE SYNONYM ddn FOR scott.emp;
--当前用户下使用私有的同义词
SELECT * FROM ddn;
--非当前用户下,对于公共的同义词可以不可直接使用,必须使用方案名.私有的同义词
SELECT * FROM scott.ddn;
--查看同义词
--当建立同义词时,Oracle会将同义词的信息存放在数据字典里。通过查询数据字典视图USER_SYNONYMS,可以显示当前用户所有同义词的详细信息
SELECT synonym_name,table_owner,table_name FROM user_synonyms;
序列:数据字典:USER_SEQUENCES 对应的同义词:SEQ
索引:数据字典:USER_INDEXES 对应的同义词:IND
同义词:数据字典:USER_SYNONYMS 对应的同义词:SYN
--删除同义词
--删除公共的同义词:
DROP PUBLIC SYNONYM synonym_name;
--删除私有同义词:
DROP SYNONYM synonym_name;
按索引列的个数:
按索引列值的唯一性:
--在一个或多个列上创建索引
CREATE INDEX index_name ON table(column[,column]....);
--创建单列索引
CREATE index idx_ename on emp(ename);
--创建复合索引
CREATE index idx_deptno_job on emp(deptno,job);
--创建唯一索引
CREATE unique index index_dname on dept(dname);
--创建非唯一索引
CREATE index idx_job on emp(job);
适合创建索引:
不适合创建索引:
--创建视图的语法格式:
CREATE [OR REPLACE] VIEW view_name [(alias[,alias]...)]--alias指字段别名,对应的是子查询中的字段内容
AS subquery--subquery指查询语句
[WITH CHECK OPTION [CONSTRAINT constraint]]--定义约束
[WITH READ ONLY];--是否只读
--创建简单视图:
--建立用于查询员工编号、姓名、工资的视图
CREATE VIEW emp_view AS SELECT empno,ename,sal FROM emp;
--创建连接视图:
--建立用于获得部门号为10的部门号,部门名称及员工信息
CREATE VIEW dept_emp_view AS SELECT d.deptno,d.dname,e.empno,e.ename,e.job,e.sal FROM emp e,dept d where d.deptno = e.deptno and d.deptno = 10;
--创建只读视图:
--建立查看10号部门员工信息的视图
CREATE VIEW emp_view AS SELECT * FROM emp WHERE deptno = 10 WITH READ ONLY;
--复杂视图
--复杂视图是包含函数、表达式或者分组数据的视图,它主要用于执行查询操作
注意:定义复杂视图时,必须要为函数或者表达式定义列别名
--创建用于获得每个岗位平均工资、工资总和、最高工资和最低工资的视图
CREATE VIEW job_view(job,avgsal,sumsal,maxsal,minsal)
AS SELECT job,avg(sal),sum(sal),max(sal),min(sal)
from emp group by job;
--在创建视图时定义CHECK约束,插入的数据必须时20号部门或者更新的数据不能改变部门号
CREATE VIEW emp_view AS SELECT empno,ename,sal FROM emp WHERE deptno - 20 WITH CHECK OPTION CONSTRAINT chk_view;
--删除视图
DROP VIEW view_name;
--视图上的DML操作:
--查询视图-select
SELECT * FROM emp_view;
--添加数据-insert
insert into emp_view(empno,ename,sal) values(8888,'qzp',6666);
--修改数据-update
UPDATE emp_view set sal = sal + 100 where empno = 8888;
--删除数据-delete
delete from emp_view where empno = 8888;
注意:针对视图的操作(insert、update、delete)实际改变的是基表中的数据
视图中存在 group by 子句,分组函数,distinct关键字,rownum 伪列不能 delete 操作
视图中存在 group by 子句,分组函数,distinct关键字,rownum 伪列、使用表达式定义的列不能 update 操作
视图中存在 group by 子句,分组函数,distinct关键字,rownum 伪列、使用表达式定义的列、视图上没有包含基表的NOT NULL 列不能能 insert 操作
触发事件:即在何种情况下触发TRIGGER
触发时间:即该TRIGGER是在触发事件之前(BEFORE)还是之后(AFTER)触发
触发器本身:即该TRIGGER被触发之后的目的和意图,正是触发器本身要做的事情
触发频率:说明触发器内定义的工作被执行的次数
--需求:每次执行删除操作之后,都会信息提示:'这是删除操作!'
SELECT * FROM user_tables;--查看所有用户表
--创建触发器
CREATE TRIGGER first_trigger
AFTER DELETE
ON dept1
BEGIN
dbms_output.put_line('这是删除的操作!!!');
END;
--删除操作触发触发器
DELETE FROM dept1 WHERE deptno = 10;
CREATE [OR REPLACE] TRIGGER tigger_name
[BEFORE | AFTER | INTEAD OF]
[DDL事件]
ON [DATABASE | SHCEMA]--DATABASE:针对整个数据库,SHCEMA:针对用户
[WHEN 触发条件]
[DECLARE]
--程序的声明部分;
BEGIN
--程序的代码部分;
END;
NO | DDL事件 | 触发时机 | 描述 |
---|---|---|---|
1 | ALTER | BEFORE/AFTER | 修改对象的结构时触发 |
2 | ANALYZE | BEFORE/AFTER | 分析数据库对象时触发 |
3 | ASSOCIATE STATISTICS | BEFORE/AFTER | 启动统计数据库对象时触发 |
4 | AUDIT | BEFORE/AFTER | 开启审核数据库对象时触发 |
5 | COMMENT | BEFORE/AFTER | 对数据库对象做注释时触发 |
6 | CREATE | BEFORE/AFTER | 创建数据库对象时触发 |
7 | DDL | BEFORE/AFTER | 针对出现的所有DDL事件都会触发 |
8 | DISASSOCIATE STATISTICS | BEFORE/AFTER | 关闭统计数据库对象时触发 |
9 | DROP | BEFORE/AFTER | 删除数据库对象时触发 |
10 | GRANT | BEFORE/AFTER | 通过SQL的GRANT命令赋予权限时触发 |
11 | NOAUDIT | BEFORE/AFTER | 禁用审计数据库对象时触发 |
12 | RENAME | BEFORE/AFTER | 通过SQL的RENAME命令对对象重命名时触发 |
13 | REVOKE | BEFORE/AFTER | 通过SQL的REVOKE命令撤销授权时触发 |
14 | TRUNCATE | BEFORE/AFTER | 通过SQL的TRUNCATE语句截断表时触发 |
--需求:禁止SCOTT用户的DDL操作
CREATE OR REPLACE TRIGGER scott_trigger
BEFORE DDL
ON SCHEMA
BEGIN
REAISE_APPLICATION_ERROR(-20005,'SCOTT用户禁止使用所有的DDL操作');
END;
案例分析:
--实现对数据库对象操作的日志记录
--步骤:
--1.创建数据库对象DDL操作日志记录表
--2.创建实现对数据库对象DDL操作记录的触发器
--3.测试
--创建一个日志表
CREATE TABLE object_log(
log_id NUMBER CONSTRAINTS pk_logid PRIMARY KEY,
operatedate DATE NOT NULL,
objecttype VARCHAR2(50) NOT NULL,
objectowner VARCHAR2(50) NOT NULL
);
--创建一个序列
CREATE SEQUENCE object_log_seq;
--创建一个触发器
CREATE OR REPLACE TRIGGER object_trigger
AFTER CREATE OR DROP OR ALTER ON SCHEMA
BEGIN
INSERT INTO object_log
(log_id, operatedate, objecttype, objectowner)
VALUES
(object_log_seq.nextval,
SYSDATE,
ora_dict_obj_type,--触发DDL数据库对象的用户
ora_dict_obj_owner);--触发DDL的数据库对象的类型
END;
--测试,创建一个序列
CREATE SEQUENCE test_log;
--查询
SELECT * FROM object_log;
语句触发器
行触发器
--语句触发器
CREATE [OR REPLACE] TRIGGER tigger_name
[BEFORE | AFTER]
[DELETE | INSERT | UPDATE [OF 列名]]
ON [DATABASE | SHCEMA]
[DECLARE]
--程序的声明部分;
BEGIN
--程序的代码部分;
END;
--行触发器
CREATE [OR REPLACE] TRIGGER tigger_name
[BEFORE | AFTER]
[DELETE | INSERT | UPDATE [OF 列名]]
ON [DATABASE | SHCEMA]
[FOR EACH ROW [WHEN 触发条件]]
[DECLARE]
--程序的声明部分;
BEGIN
--程序的代码部分;
END;
--示例一:实现数据安全保护(数据的安全性检查)
--需求;非工作日不可对员工信息进行更改
CREATE OR REPLACE TRIGGER emp_trigger1
BEFORE INSERT OR UPDATE OR DELETE
ON emp
BEGIN
IF to_char(sysdate,'day') IN ('星期六','星期日')THEN
RAISE_APPLICATION_ERROR(-20006,'不能在休息日改变员工信息!');
END IF;
END;
--测试
delete from emp where empno = 7788;
ORA-20006: 不能在休息日改变员工信息!
ORA-06512: 在 "SCOTT.EMP_TRIGGER1", line 3
ORA-04088: 触发器 'SCOTT.EMP_TRIGGER1' 执行过程中出错
--示例二:实现数据审计
--需求:审计员工信息表数据的变化,审计删除时间,以及被删除的雇员名
--创建审计表
CREATE TABLE delete_emp_audit(
name VARCHAR2(10),
delete_time DATE
);
--创建触发器
CREATE OR REPLACE TRIGGER del_emp_trigger
AFTER DELETE ON emp
FOR EACH ROW
BEGIN
INSERT INTO delete_emp_audit VALUES(:old.ename,SYSDATE);
END;
--测试
DELETE FROM emp WHERE empno = 7499;
select * from delete_emp_audit;
--示例三:实现数据完整性(数据确认):数据完整性用于确认数据满足商业逻辑或企业规则,实现数据完整性首选约束,约束无法实现的,可以使用触发器实现数据完整性。
--需求:要求员工涨工资后工作不能低于原来的工资,并且所涨的工资不能超过原工资的50%
CREATE OR REPLACE TRIGGER tr_check_sal
BEFORE UPDATE OF sal ON emp
FOR EACH ROW
WHEN (new.salold.sal*1.5)
BEGIN
RAISE_APPLICATION_ERROR(-20028,'工资只升不降,并且升幅不能超过50%');
END;
--测试
UPDATE emp SET sal = sal*1.8 WHERE empno = 7788;
ORA-20028: 工资只升不降,并且升幅不能超过50%
ORA-06512: 在 "SCOTT.TR_CHECK_SAL", line 2
ORA-04088: 触发器 'SCOTT.TR_CHECK_SAL' 执行过程中出错
--示例四:实现参照完整性(比如级联更新)
--需求:级联更新DEPT表的主键列以及EMP表的外部键列
CREATE OR REPLACE TRIGGER upd_cascade_trigger
AFTER UPDATE OF deptno
ON dept
FOR EACH ROW
BEGIN
UPDATE emp SET deptno = :new.deptno WHERE deptno = :old.deptno;
END;
--测试
UPDATE dept SET deptno = 50 WHERE deptno = 10;
select deptno,ename from emp where deptno = 50;
--案例6
CREATE OR REPLACE TRIGGER modify_stu
AFTER INSERT OR DELETE OR UPDATE OF stu_name -- 字段
ON student_123 -- 表
FOR EACH ROW -- 行级触发
BEGIN
IF INSERTING THEN
INSERT INTO stu_log_12 VALUES(1,'insert',SYSDATE,:NEW.stu_name);
ELSIF DELETING THEN
INSERT INTO stu_log_12 VALUES(2,'delete',SYSDATE,:OLD.stu_name);
ELSIF UPDATING THEN
INSERT INTO stu_log_12 VALUES(3,'update_old',SYSDATE,:OLD.stu_name);
INSERT INTO stu_log_12 VALUES(4,'update_new',SYSDATE,:NEW.stu_name);
END IF;
END;
Select * From stu_log_12
INSERT INTO student_123 VALUES(1,'NO2','李四',21,'数学系'); --插入一条数据
DELETE student_123 WHERE stu_name='张三'; --删除一条数据
UPDATE student_123 SET stu_age=19 WHERE stu_name='李四'; --修改李四的年龄
UPDATE student_123 SET stu_name='王二' WHERE stu_name='李四';--修改李四的名称
Select * From stu_log_12
Select * From student_123
CREATE OR REPLACE TRIGGER T_NAME
BEFORE DELETE ON EMP
FOR EACH ROW
BEGIN
IF DELETING AND
SYSDATE < TO_DATE('20201028100000', 'yyyy-mm-dd HH24:mi:ss') THEN
RAISE_APPLICATION_ERROR(-20001, '该表不允许删除数据');
END IF;
END;
Delete From emp
--创建视图
CREATE OR REPLACE VIEW emp_dept
AS
SELECT d.deptno,d.dname,e.empno,e.ename
FROM dept d, emp e
WHERE d.deptno = e.deptno;
--创建替代触发器
CREATE OR REPLACE TRIGGER instead_of_trigger
INSTEAD OF
INSERT
ON emp_dept
FOR EACH ROW
DECLARE
v_temp INT;
BEGIN
SELECT COUNT(*) INTO v_temp FROM dept WHERE deptno = :new.deptno;
IF v_temp = 0 THEN
INSERT INTO dept(deptno,dname)VALUES(:new.deptno,:new.dname);
END IF;
SELECT COUNT(*) INTO v_temp FROM emp WHERE empno = :new.empno;
IF v_temp = 0 THEN
INSERT INTO emp(empno,ename,deptno) VALUES(:new.empno,:new.ename,:new.deptno);
END IF;
END;
--测试
INSERT INTO emp_dept VALUES(50,'DEVELOPMENT',2222,'ALICE');
SELECT * FROM EMP_DEPT
--连接sys用户
con sys/as sysdba;
--创建事件表
CREATE TABLE event_table(
event varchar2(50),
event_time date
);
--再创建一个系统触发器
create or replace trigger startup_trigger
after startup on database
begin
insert into event_table values(ora_sysevent,SYSDATE);
end;
--在SQLPLUS窗口执行下列命令
SHUTDOWN
STARTUP
SELECT * FROM event_table;
oracle备份:
物理备份
逻辑备份(导入导出)
--在SQLPLUS中执行如下命令
--导出
--将数据库orcl完全导出
exp system/oracle@orcl file=c:\oracle_bak\orcl_bak.dmp full=y
--将数据库中scott用户的所有对象导出
exp scott/tiger1@orcl file=c:\oracle_bak\scott_bak.dmp owner=scott
--将scott用户中表emp,dept导出
exp scott/tiger1@orcl file=c:\oracle_bak\table_bak.dmp tables=(emp,dept)
--导入
--联机帮助命令:
IMP HELP = Y;
--将备份文件导入到数据库
imp scott/tiger1@orcl file=c:\oracle_bak\scott_bak.dmp ignore=y
--将scott用户的备份文件导入到yanln用户中
imp yanln/yanln@orcl fromuser=scott touser=yanln file=c:\oracle_bak\scott_bak.dmp
触发器导入导出文件
--创建员工表的备份表
CREATE TABLE emp_bak
AS
SELECT * FROM emp;
--创建触发器来实现数据的同步备份
--当删除员工后,备份表同步删除
CREATE OR REPLACE TRIGGER syno_bak_trigger
AFTER DELETE
ON emp
FOR EACH ROW
BEGIN
DELETE FROM emp_bak WHERE empno = :old.empno;
END;
--测试
SELECT * FROM EMP;
DELETE FROM emp WHERE empno = 7499;
select * from emp_bak;
rollback;