--day01--
张淑敏
zhangsm@tedu.cn
Oracle
sql: 4天
plsql: 2天
proc: 2天
1. 数据库介绍
1.1 数据库简介
1.1.1 数据管理技术的发展
人工管理阶段:20世纪50年代中期之前
文件管理阶段:20世纪的50年代末期到60年代中期
缺点:数据冗余
数据的不准确
数据之间的联系弱
数据库管理阶段:
数据库技术诞生的三大标志性事件:
1968年,IBM公司---> IMS
1969年,DBTG发布了一系列的报告---> 标准和规范
1970年,IBM的研究员E.F.Codd 发表了一系列论文
提出了关系模型
特点:采用复杂的结构化的数据模型
最低的冗余度
数据完整性
数据库系统为用户提供了方便的接口
控制功能:
并发控制
数据库的恢复
安全性
系统更加灵活
信息处理方式不在以程序为中心,而是以数据为中心
1.1.2 和数据库相关的几个术语
数据(data):数据库存储的基本对象,包括文字、图形、声音、视频等
数据库(database):长期存储在计算机内、有组织、可共享的大量数据的集合
数据库管理系统(DBMS):是位于操作系统和用户之间的一层应用程序,科学的组织、存储数据,高效的获取和维护数据
数据库系统(DBS): 由数据库、数据库管理系统、应用程序和数据库管理员(或用户) 组成的系统。
数据库管理员: DBA
1.1.3 关系数据库
用二维表保存数据的数据库
表头 字段
行
列
字段值
1.2 主流的关系型数据库产品
商业型
Oracle Oracle(甲骨文) 10g 11g 12c
DB2 IBM
sql server 微软
sybase
开源
mysql Oracle
1.3 sql语言
SQL(Structured Query Language): 结构化查询语言
SQL分为:
数据查询语言(DQL): select
用来查询数据中的数据 使用最广泛、语法最灵活
数据操作语言(DML): insert delete update
用来改变数据库中的数据
数据定义语句(DDL): create drop alter
用来创建、删除、修改数据库对象
事务控制语句(TCL): commit rollback savepoint
用来保证数据的一致性
数据控制语句(DCL): grant、revoke、create user
用来执行权限的授予和回收、创建用户等
1.4 远程登录服务器
1.4.1 开发工具
sql*plus
sqlplus:oracle提供,和数据库进行交互的工具
命令提示符下的工具
oracle sqldeveloper: 可视化工具
1.4.2 远程登录
第一步:远程登录服务器
telnet ip
用户名:
密码:
第二步:使用sqlplus工具
sqlplus
输入用户名:
输入密码:
现场班:
telnet 172.60.5.80 或172.60.5.81
用户:oracle
密码: oracle
sqlplus
输入用户名: openlab
输入密码: open123
SQL>
sqlplus命令:可以不以分号结尾
sql语句: 必须以分号结尾(学习的内容)
1.5 描述表结构
sqlplus命令:desc
desc 表名[;]
desc s_emp;
Name Null? Type
------------------------------- --------------- -------------
ID 员工编号 NOT NULL NUMBER(7)
LAST_NAME 姓 NOT NULL VARCHAR2(25)
FIRST_NAME 名 VARCHAR2(25)
USERID VARCHAR2(8)
START_DATE 入职日期 DATE
COMMENTS VARCHAR2(255)
MANAGER_ID 领导编号 NUMBER(7)
TITLE 职位 VARCHAR2(25)
DEPT_ID 部门编号 NUMBER(7)
SALARY 工资 NUMBER(11,2)
COMMISSION_PCT 提成 NUMBER(4,2)
Name:表中的字段名
Null? 该字段是否允许为空
NOT NULL:不允许为空
Type: 字段的数据类型
number(p,s) 数字类型
p: 有效数字的位数(从第一个非零数字开始)
1<=p<=38 默认38
s: 精度
-84<=s<=127
number ---> number(38,0)
number(p) ----> number(p,0)
number(p,s)
varchar2(n) 变长字符串 n不能省略 1~4000bytes
char(n) 定长字符串 n默认为1 1~2000bytes
date 日期
2. select语句
2.1 几个概念
选择:选中部分行,全部列
投影:选中全部行,部分列
表连接:需要的数据来自于多张表
2.2 select语句的子句
基本的查询语句: select..from
where子句
order by子句
单行函数
表连接
组函数和分组
子查询
3.select..from语句
select 字段列表 from 表名;
3.1 列出表中一个字段
select 字段名 from 表名;
-- 列出所有员工的工资
select salary from s_emp;
3.2 列出表中的多个字段
select 字段名,字段名,.... from 表名;
-- 列出员工的编号、名字、职位、工资
select id,first_name,title,salary from s_emp;
3.3 列出表中全部字段
--
select id,last_name,first_name,userid,..... from s_emp;
-- 使用 * 代替所有字段
select * from s_emp;
3.4 算数运算 一般指的是数字类型
+ - * /
-- 列出员工的信息,包括编号、名字、工资、年收入
12*salary + 1000
select id,first_name,salary,12*salary+1000 from s_emp;
3.5 字段或表达式 命名 别名
3.5.1 语法
字段或表达式 [as] 别名
select id,first_name as name,salary,
12*salary+1000 yearsal from s_emp;
3.5.2 使用 ""
-- 屏蔽特殊字符或关键字等
select id,first_name as name,salary,
12*salary+1000 "year sal" from s_emp;
-- 大小写敏感
select id,first_name as name,salary,
12*salary+1000 "YearSal" from s_emp;
3.6 sql中的字符串
3.6.1 字符串的表示方式
单引号:‘‘
‘a‘ ‘Hello world‘
3.6.2 字符串的拼接
||
-- 把员工的first_name和last_name拼接起来
select id,first_name||last_name as name from s_emp;
-- 把员工的first_name和last_name之间拼接一个 .
select id,first_name||‘.‘||last_name as name
from s_emp;
-- 把员工的first_name和last_name之间拼接一个 ‘
使用转义字符:‘
select id,first_name||‘‘‘‘||last_name as name
from s_emp;
3.7 NULL值的处理
-- 计算员工的年收入,考虑提成
12*salary + 12*salary*commission_pct/100
12*salary*(1+commission_pct/100)
-- 下面语句的结果集是错误的
select id,first_name,title,
12*salary*(1+commission_pct/100) as yearsal
from s_emp;
NULL值参与运算的表达式的结果为空
使用函数 nvl 处理NULL值
nvl(par1,par2) : 当par1不为空,返回par1
放par1为空,返回par2
nvl(12*salary*(1+commission_pct/100),12*salary)
12*salary*(1+nvl(commission_pct,0)/100)
select id,first_name,salary,
12*salary*(1+nvl(commission_pct,0)/100) as yearsal
from s_emp;
3.8 排重 distinct
-- 列出员工的职位
select distinct title from s_emp;
-- 多列排重
select distinct title,dept_id from s_emp;
4. where子句
select ...
from ..
where 条件;
4.1 作用
根据条件对表中的数据进行筛选,挑选出符合条件的行
4.2 数字类型的条件
-- 列出工资大于1400的员工的信息
select id,first_name,salary from s_emp
where salary>1400;
4.3 字符串类型的条件
-- 列出名字为‘Ben‘的员工的信息
select id,first_name,salary from s_emp
where first_name = ‘Ben‘; -- 有一行结果
select id,first_name,salary from s_emp
where first_name = ‘ben‘; -- 没有结果
-- sql中没有 ==
-- sql中不区分大小写,但是字符串的值是区分的
4.4 比较运算符
> < >= <= = !=(<> ^=)
4.5 sql提供的运算符
4.5.1 表示一个闭区间 [a,b]
between a and b
-- 列出工资在[1100,1550] 之间的员工的信息
select id,first_name,salary from s_emp
where salary between 1100 and 1550;
4.5.2 表示一个列表
in(值1,值2,...)
address in(‘北京‘,‘上海‘,‘广州‘)
-- 列出部门编号为 31,42,50的员工的信息
select id,first_name,dept_id from s_emp
where dept_id in(31,42,50);
4.5.3 模糊查询
like ‘包含通配符的字符串‘
通配符:
%: 任意长度的任意字符
_: 一位任意字符
StuName like ‘李_%‘
-- 列出first_name首字母为‘M‘的员工的信息
select id,first_name from s_emp
where first_name like ‘M%‘;
-- 列出first_name第二个字母为‘a‘的员工的信息
select id,first_name from s_emp
where first_name like ‘_a%‘;
user_tables: 数字字典 保存当前用户的所有表的信息
desc user_tables;
-- 列出user_tables中以‘S_‘开头的表的信息
select table_name from user_tables
where table_name like ‘S_%‘;
-- 使用转义字符 escape
select table_name from user_tables
where table_name like ‘S\_%‘ escape ‘\‘;
4.5.4 空值的判断
is null
-- 列出manager_id为空的员工的信息
select id,first_name,title from s_emp
where manager_id = null;
select id,first_name,title from s_emp
where manager_id != null;
-- 使用=或!=判断null值,结果永远为假
select id,first_name,title from s_emp
where manager_id is null;
4.6 sql中的逻辑运算符
and or not
-- 使用and改写 between and 案例
列出工资在[1100,1550]之间的员工的信息
select id,first_name,salary from s_emp
where salary between 1100 and 1550;
select id,first_name,salary from s_emp
where salary>=1100 and salary<=1550;
-- 使用or改写 in 案例
列出编号为31,42,50的部门的员工的信息
select id,first_name,dept_id from s_emp
where dept_id in(31,42,50);
select id,first_name,dept_id from s_emp
where dept_id=31 or dept_id=42 or dept_id=50;
对立面:
> <=
< >=
= !=(<> ^=)
between and not between and
in not in(注意NULL值)
like not like
is null is not null
-- 列出有提成的员工的信息
select id,first_name,commission_pct from s_emp
where commission_pct is not null;
5. order by子句
5.1 总是出现在一条select语句的最后
select 字段列表
from 表名
where 子句
......
order by子句;
5.2 语法
order by 排序标准 排序方式
排序方式:
asc 升序 (自然顺序、字典顺序) 默认排序方式
desc 降序
5.3 按照工资降序排序,列出dept_id为31,32,33的员工的信息
select id,first_name,salary,dept_id from s_emp
where dept_id in(31,32,33)
order by salary desc;
5.4 多列排序
select id,first_name,salary from s_emp
order by salary;
order by 排序标准1 排序方式,排序标准2 排序方式
select id,first_name,salary from s_emp
order by salary,id desc;
-- 多列排序时,每一个排序标准的排序方式是自己定义的
5.5 排序时,默认按照最大值处理
-- 根据manager_id排序,列出员工的信息
select id,first_name,manager_id from s_emp
order by manager_id desc;
-----------------------------------------------------------------总结
1.数据库介绍
sql语言
sqlplus的命令:desc
数据类型
2.select语句
3.select...from
4.where子句
比较运算符:> < >= <= = !=(<> ^=)
sql提供的运算符
between and
in
like
is null
sql中的逻辑运算符
and or not
5. order by子句
----------------------------------------------------------------
练习:
1. 查看s_dept和s_region表的表结构
2. 查询s_dept和s_region表中的数据
3. 计算员工的年收入,列出年收入大于15000的员工的信息,
并对年收入进行降序排序。
--day02--
回顾:
1.数据库介绍
sql:
dql: select
dml: insert delete update
ddl: create drop alter
tcl: commit rollback savepoint
dcl: create user、grant、revoke
desc s_dept;
2.from子句
select name from s_dept;
select id,name from s_dept;
select * from s_dept;
select id,12*salary+1000 from s_emp;
select id,12*salary + 1000 as "yearsal" from s_emp;
select id,first_name||‘‘‘‘||last_name name from s_emp;
select id,12*salary*(1+nvl(commission_pct,0)/100) yearsal from s_emp;
select distinct title,dept_id from s_emp;
3.where子句
select id,first_name,salary from s_emp
where salary>1400;
select id,first_name from s_emp
where first_name=‘Ben‘;
select id,first_name,salary from s_emp
where salary between 1100 and 1550;
select id,first_name,dept_id from s_emp
where dept_id in(31,42,50);
select id,first_name from s_emp
where first_name like ‘_a%‘;
select table_name from user_tables
where table_name like ‘S\_%‘ escape ‘\‘;
select id,first_name,manager_id from s_emp
where manager_id is null;
and or not
select id,first_name,salary from s_emp
where salary>=1100 and salary<=1550;
select id,first_name,dept_id from s_emp
where dept_id=31 or dept_id=42 or dept_id=50;
select id,first_name,commission_pct from s_emp
where commission_pct is not null;
4.order by子句
select id,first_name,salary from s_emp
order by salary desc,id;
select id,first_name,start_date from s_emp
order by start_date;
练习:
select id,first_name,salary,
12*salary*(1+nvl(commission_pct,0)/100) as yearsal
from s_emp
where 12*salary*(1+nvl(commission_pct,0)/100)>15000
order by yearsal desc;
-----------------------------------------------------------------
1.单行函数
1.1 单行函数和组函数的概念
单行函数:针对sql语句影响的数据,每行都做处理,每行产生一个结果
select upper(first_name) from s_emp
where id<11;
组函数:针对sql语句影响的数据,每组做处理,每组产生一个结果
select count(first_name) from s_emp
where id<11;
1.2 dual表
desc dual;
1.3 字符串函数
upper(s): 把参数s中的英文字母转换成大写 返回
select upper(‘hello world!‘) from dual;
lower(s): 把参数s中的英文字母转换成小写 返回
select lower(‘hello world!‘) from dual;
initcap(s):把参数s中的每个单词转换成首字母大写、其余小写的形式
select initcap(‘hello world!‘) from dual;
concat(s1,s2): 字符串连接 ||
substr(s,start[,length]):从start位置开始,截取字符串s中的length的字符
start:开始位置 从1计数 如果写成0,按照1处理
>0 表示从左侧开始计数
<0 表示从右侧开始计数
length: 截取的子字符串的长度
缺省时,表示截取到字符串的最后
select substr(‘hello world!‘,-6,4) from dual;
length(s): 返回字符串的长度
select length(‘hello world!‘) from dual;
练习:使用两种方式 列出s_emp中first_name的后三位
select first_name,substr(first_name,-3) from s_emp;
select first_name,
substr(first_name,length(first_name)-2) from s_emp;
1.4 数字函数
round(x[,y]) : 四舍五入
y: 缺省时, 0 round(4.56) = 5
>0 四舍五入到小数点后y位 round(4.56,1) = 4.6
<0 四舍五入到小数点前|y|位 round(456.78,-2) =500
trunc (x[,y]) :截取
y: 缺省时, 0 trunc(4.56) = 4
>0 截取到小数点后y位 trunc(4.56,1) = 4.5
<0 截取到小数点前|y|位 trunc(456.78,-2) =400
select trunc(456.67,-2) from dual;
floor(x) : 不大于x的最大整数
ceil(x):不小于x的最小整数
select floor(4.5) from dual; <<5
select ceil(4.5) from dual; >>4
1.5 日期类型和日期函数
1.5.1 日期类型 date
默认格式:
英文: dd-MON-yy 12-OCT-17
中文: dd-n月-yy 12-10月-17
系统时间: sysdate
select sysdate from dual;
date数据各部分的格式表示:
cc 世纪 21
yy 2位数字的年 17
yyyy 4位数字的年 2017
year 年份的英文全拼 twenty seventeen
mm 2位数字的月 10
mon(MON) 月份单词的前三个字母 oct(OCT)
month(MONTH) 月份单词的全拼 october(OCTOBER)
dd 2位数字的天 12
dy 星期的单词的前三个字母 thu
day 星期单词的全拼 thursday
hh 12小时制的小时 02
hh24 24小时制的小时 14
mi 分钟 34
ss 秒 15
2017-10-12 ‘yyyy-mm-dd‘
1.5.2 日期类型的算术运算 (在day上进行操作)
1) 日期 + 数字
select sysdate + 80 from dual;
2) 日期 - 数字
select sysdate - 200 from dual;
3) 日期1 - 日期2
select sysdate - to_date(‘01-JAN-00‘) from dual;
1.5.3 常用的日期函数
add_months(d,n):在日期d上加n个月
select add_months(sysdate,4) from dual;
months_between(d1,d2):返回两个日期相差的月数
select months_between(sysdate,‘01-JAN-00‘)
from dual;
next_day(d,dy):返回日期d的下一个dy(星期几)
select next_day(sysdate,‘FRIDAY‘) from dual;
select next_day(sysdate,‘FRI‘) from dual;
select next_day(sysdate,6) from dual;
使用1~7 对应 星期日~星期六
last_day(d): 返回日期d所在月份的最后一天
select last_day(sysdate) from dual;
1.6 转换函数
1.6.1 to_char
to_char(d|n[,fmt]): 把日期或数字按照给定的格式转换成字符串
1) 日期--> 字符串
select to_char(sysdate,‘yyyy-mm-dd hh:mi:ss am‘)
from dual;
select id,first_name,to_char(start_date,‘yyyy-mm-dd‘)
from s_emp
order by start_date;
2) 数字 ---> 字符串
格式:
9 小数点前代表0-9,小数点后代表1-9
0 小数点前代表前导0,小数点后代表0-9
. 小数点
, 分隔符
$ 美元符号
L 本地货币符号
格式字符串以 fm开头,比如:fm$099,999.00
select to_char(1234,‘fm$099,999.00‘) from dual;
1.6.2 to_number
to_number(s[,fmt])
select to_number(‘$001,234.00‘,‘fm$099,999.00‘)
from dual;
-- 隐式转换
select id,first_name,salary from s_emp
where id=‘1‘;
1.6.3 to_date
to_date(s[,fmt]) 字符串 ----> 日期
create table testdate_zsm_00(
id number,
start_time date
);
insert into testdate_zsm_00 values(1,‘12-OCT-17‘);
insert into testdate_zsm_00
values(2,to_date(‘2017-10-12 16:22‘,‘yyyy-mm-dd hh24:mi‘));
select id,to_char(start_time,‘yyyy-mm-dd hh24:mi‘)
from testdate_zsm_00;
1.7 函数嵌套
一个函数的返回值作为另一个函数的参数
-- 练习: 列出每个员工的id,first_name和manager_id,
如果manager_id为null,显示成‘BOSS‘
nvl : 参数可以是任意类型,但是两个参数的类型必须一致
to_char
select id,first_name,
nvl(to_char(manager_id),‘BOSS‘) mid from s_emp;
2.表连接
2.1 需求:列出每个员工的id,first_name和所在部门的名称
1) 列出每个员工的id,first_name和所在部门的编号
select id,first_name,dept_id from s_emp;
2) 需要查询的数据来自于两张表
desc s_dept;
Name Null? Type
----------------------------- ------------- ------------
ID 部门编号 NOT NULL NUMBER(7)
NAME 部门名称 NOT NULL VARCHAR2(25)
REGION_ID 地区编号 NUMBER(7)
s_emp: id,first_name
s_dept: name
select * from s_dept;
select s_emp.id,s_emp.first_name,s_dept.name
from s_emp,s_dept
where s_emp.dept_id = s_dept.id;
笛卡尔积: 从多张表中获取数据时,没有表连接的条件的结果集,称为笛卡尔积
一般把表连接的筛选条件称为连接条件(关联条件)
表连接的基本语法:
select 字段列表
from 表1,表2,..
where 关联条件;
2.2 表的别名
表名 别名
select e.id,e.first_name,d.name
from s_emp e,s_dept d
where e.dept_id = d.id;
-- 表一旦命名别名,则原表名在当前语句中失效
select e.id,e.first_name,d.name
from s_emp e,s_dept d
where e.dept_id = s_dept.id; -- 错误
-- 如果关联的多张表中,字段名没有冲突,可以省略该字段名前的表名或别名
select e.id,first_name,name
from s_emp e,s_dept d
where dept_id = d.id;
2.3 表连接的分类
内连接:符合关联条件的数据才回出现在结果集中
外连接:内连接的结果集 + 匹配不上的数据
2.4 内连接
2.4.1 等值连接
关联条件中使用的运算符是 =
desc s_region;
Name Null? Type
---------------------------- ------------ ------------
ID 地区编号 NOT NULL NUMBER(7)
NAME 地区名称 NOT NULL VARCHAR2(50)
-- 练习:列出每个部门及其所在地区的信息,包括
部门编号、部门名称、所在地区的名称
s_dept: id,name
s_region:name
关联字段:s_dept region_id
s_region id
select d.id,d.name dname,r.name rname
from s_dept d,s_region r
where d.region_id = r.id;
2.4.2 非等值连接
关联条件中使用的运算符不是 =
需求:列出员工及其工资级别的信息
创建一个工资级别表:salgrade
create table salgrade(
grade number(7) primary key,
losal number(11,2),
hisal number(11,2)
);
插入数据:
insert into salgrade values(1,700,1200);
insert into salgrade values(2,1201,1400);
insert into salgrade values(3,1401,2000);
insert into salgrade values(4,2001,3000);
insert into salgrade values(5,3001,9999);
commit;
select e.id,e.first_name,e.salary,g.grade
from s_emp e,salgrade g
where e.salary between g.losal and g.hisal;
2.4.3 自连接
在逻辑上把一张表当成两张表使用
使用自连接时,表必须命名别名
需求:列出s_emp表中所有的领导的信息
1)列出员工及其领导的信息
select e.id,e.first_name,m.id,m.first_name
from s_emp e,s_emp m
where e.manager_id = m.id;
2) 去掉员工的相关信息,排重
select distinct m.id,m.first_name
from s_emp e,s_emp m
where e.manager_id = m.id;
2.5 外连接
(+) -- oracle中外连接的特有用法
表1.字段(+) 运算符 表2.字段:
内连接的结果集 + 表2中匹配不上的数据
表1.字段 运算符 表2.字段(+)
内连接的结果集 + 表1中匹配不上的数据
匹配不上的这部分数据,另一张表中所有字段添null
2.5.1 自连接
需求:列出普通员工的信息
1)把普通员工的信息加入到结果集
select e.id,e.first_name,m.id,m.first_name
from s_emp e,s_emp m
where e.manager_id(+) = m.id;
2) 从结果集中筛选出普通员工
select m.id,m.first_name
from s_emp e,s_emp m
where e.manager_id(+) = m.id
and e.id is null;
2.5.2 等值连接
-- 把id=1的部门改为null
update s_emp set dept_id=null where id=1;
commit;
-- 列出所有员工的id,first_name和所在部门名称
select e.id,e.first_name,d.name
from s_emp e,s_dept d
where e.dept_id = d.id(+);
2.5.3 非等值连接
-- 把id=1的员工的工资改为10000
update s_emp set salary = 10000 where id=1;
commit;
-- 列出所有员工的工资级别信息
select e.id,e.first_name,e.salary,g.grade
from s_emp e,salgrade g
where e.salary between g.losal(+) and g.hisal(+);
select e.id,e.first_name,e.salary,g.grade
from s_emp e,salgrade g
where e.salary(+)>=g.losal and e.salary(+)<=g.hisal;
-----------------------------------------------------------------
练习:
-- 向部门表中添加一行数据
insert into s_dept(id,name) values(110,‘Test‘);
commit;
1. 列出所有没有员工的部门的信息
2. 列出员工的编号、名称、以及工作地区的名称
三表查询:
select 字段列表
from 表1,表2,表3
where 关联条件1 and 关联条件2;
--day03--
回顾:
1.单行函数
2.表连接
oracle中的表连接
内连接
等值连接
select e.id,e.first_name,d.name
from s_emp e,s_dept d
where e.dept_id = d.id;
select d.id,d.name dname,r.name rname
from s_dept d,s_region r
where d.region_id = r.id;
select e.id,e.first_name,r.name
from s_emp e,s_dept d,s_region r
where e.dept_id=d.id and d.region_id = r.id;
非等值连接
select e.id,e.first_name,e.salary,g.grade
from s_emp e,salgrade g
where e.salary between g.losal and g.hisal;
自连接
select distinct m.id,m.first_name
from s_emp e,s_emp m
where e.manager_id = m.id;
外连接
等值连接
select e.id,e.first_name,d.name
from s_emp e,s_dept d
where e.dept_id = d.id(+);
select d.id,d.name dname,r.name rname
from s_dept d,s_region r
where d.region_id = r.id(+);
非等值连接
select e.id,e.first_name,e.salary,g.grade
from s_emp e,salgrade g
where e.salary between g.losal(+) and g.hisal(+);
自连接
select m.id,m.first_name
from s_emp e,s_emp m
where e.manager_id(+) = m.id
and e.id is null;
select d.id,d.name
from s_emp e,s_dept d
where e.dept_id(+) = d.id
and e.id is null;
-----------------------------------------------------------------
1.sql99标准中的表连接
内连接:
select 字段列表
from 表1 [inner] join 表2
on 关联条件;
select e.id,e.first_name,d.name
from s_emp e inner join s_dept d
on e.dept_id = d.id;
三表连接:
select 字段列表
from 表1 join 表2 on 关联条件1
join 表3 on 关联条件2;
select e.first_name,d.name,r.name
from s_emp e join s_dept d on e.dept_id = d.id
join s_region r on d.region_id = r.id;
外连接:
左外连接:内连接的结果集 + 左表匹配不上的数据
select 字段列表
from 左表 left [outer] join 右表
on 关联条件;
select e.id,e.first_name,d.name
from s_emp e left join s_dept d
on e.dept_id = d.id;
右外连接:内连接的结果集 + 右表匹配不上的数据
select 字段列表
from 左表 right [outer] join 右表
on 关联条件;
select e.id,e.first_name,d.name
from s_emp e right join s_dept d
on e.dept_id = d.id;
全外连接:内连接的结果集 + 两表匹配不上的数据
select 字段列表
from 左表 full [outer] join 右表
on 关联条件;
select e.id,e.first_name,d.name
from s_emp e full join s_dept d
on e.dept_id = d.id;
select d.id,d.name
from s_emp e right join s_dept d
on e.dept_id = d.id
where e.id is null;
2.集合运算(合并结果集)
union: 两个结果集取并集 并排重、排序
union all:两个结果集直接取并集
select id from s_emp union
select id from s_dept;
select id from s_emp union all
select id from s_dept;
intersect: 取两个结果集的交集
select id from s_emp intersect
select id from s_dept;
minus: 第一个结果集 - 第二个结果集
select id from s_emp minus
select id from s_dept;
-- 两个结果集的字段列表的数量和数据类型必须匹配
select id,first_name from s_emp minus
select id,name from s_dept;
select id,first_name from s_emp union
select null,name from s_dept;
3. 组函数和分组
3.1 组函数
1) 常用的组函数
count(par|*) : 统计一组数据的行数
参数可以是任何类型 还可以是*
-- 统计工资高于1500的员工数
select count(*) from s_emp where salary>1500;
max(par) : 统计一组数据中的最大值
min(par):统计一组数据中的最小值
参数可以是数字、字符串、日期类型
-- 列出最早和最晚入职的员工
select max(to_char(start_date,‘yyyy-mm-dd‘)),
min(to_char(start_date,‘yyyy-mm-dd‘))
from s_emp;
sum(par) : 统计一组数据的和
avg(par) : 统计一组数据的平均值
参数是数字类型
-- 列出销售部的总工资和平均工资
select sum(salary),avg(salary)
from s_emp e,s_dept d
where e.dept_id = d.id and d.name=‘Sales‘;
2) 组函数对null的处理: 忽略
select count(commission_pct) from s_emp;
3) 组函数 可以排重
select sum(salary),sum(distinct salary) from s_emp;
select count(salary),count(distinct salary) from s_emp;
3.2 分组
3.2.1 语法
group by 分组标准
.....
where ....
group by ...
...
select
order by
3.2.2 分组统计每个部门的人数
select dept_id,count(*) cnt
from s_emp
group by dept_id;
/* 分组语句中列出的字段必须是分组标准
或者是组函数的参数*/
select id,dept_id,count(*) cnt
from s_emp
group by dept_id; -- 错误
3.2.2 多列分组
分组统计每个部门的人数,显式部门的名称和人数
select e.dept_id,d.name,count(e.id) cnt
from s_emp e join s_dept d on e.dept_id = d.id
group by e.dept_id,d.name;
分组统计每个部门的人数,列出人数超过2个的部门
select dept_id,count(*) cnt
from s_emp
-- 错误:where子句中不能使用组函数
-- where count(*)>2
group by dept_id;
3.2.3 having子句
分组后,根据条件筛选出符合条件的组
select dept_id,count(*) cnt
from s_emp
where 1=1
group by dept_id
having count(*)>2
order by cnt;
-- 语法顺序
select 字段列表
from 表名
where 条件 -- 从表中根据条件筛选符合条件的行
group by 分组标准 -- 根据分组标准分成多个组
having 条件 -- 从分组结果中根据条件筛选符合条件的组
order by 排序标准 排序方式;
-- 执行顺序
from
where
group by
having
select
order by
-- 练习:列出平均工资大于1000 的部门的信息
select dept_id,avg(salary) avgsal
from s_emp
group by dept_id
having avg(salary)>1000;
select d.id,d.name,avg(e.salary) avgsal
from s_emp e,s_dept d
where e.dept_id = d.id
group by d.id,d.name
having avg(e.salary)>1000
order by avgsal;
4. 子查询
子查询指的是把一条select语句嵌入到另一条sql语句中
执行时,先执行嵌入的子查询,然后再执行外层的sql语句
4.1 where子句
1) 单行单列的子查询
-- 列出工资比‘Ben‘高的员工的信息
a. 列出‘Ben‘的工资
select salary from s_emp where first_name=‘Ben‘;
-- 1100
b. 列出工资高于‘Ben‘的员工的信息
select id,first_name,salary from s_emp
where salary>1100;
c. 合并
select id,first_name,salary from s_emp
where salary>(
select salary from s_emp where first_name=‘Ben‘
);
2) 多行单列的结果集
子查询的结果集为多值时,不能使用比较运算符
需要使用处理多值的运算值,比如in、not in、any、all等
(any、all要和比较运算符配合使用:>any、<all)
-- 使用子查询列出所有的领导的信息
a) 列出领导的编号
select distinct manager_id from s_emp;
-- null,1,2,3,6,7,8,9,10
b) 根据编号,列出领导的信息
select id,first_name,title from s_emp
where id in(null,1,2,3,6,7,8,9,10);
c) 合并
select id,first_name,title from s_emp
where id in(
select distinct manager_id from s_emp);
-- 使用子查询列出普通员工的信息
select id,first_name,title from s_emp
where id not in(
select distinct manager_id from s_emp
where manager_id is not null);
3) 使用exists
-- 列出有员工的部门的信息
select * from s_dept d where exists(
select * from s_emp e where e.dept_id=d.id
);
4.2 having子句
-- 列出平均工资高于公司平均工资的部门的信息
select dept_id,avg(salary) from s_emp
group by dept_id
having avg(salary)>(
select avg(salary) from s_emp
);
4.3 from子句
一个select语句产生的结果集,可以看成是一个内视图或者匿名视图,只能在当前语句使用
select id,name,yearsal from
(select id,first_name name,12*salary+1000 yearsal from s_emp) e
where yearsal>15000;
-- 列出工资高于本部门平均工资的员工的信息
a) 列出每个部门的编号及其平均工资
select dept_id,avg(salary) avgsal from s_emp
group by dept_id;
-- 相当于包含dept_id和avgsal两个字段的一张表 s
b) 使用表连接(s_emp、s) 实现功能
s_emp: id,first_name,salary
s: avgsal
关联字段: s_emp.dept_id和s.dept_id
select e.id,e.first_name,e.salary,s.avgsal
from s_emp e,(
select dept_id,avg(salary) avgsal
from s_emp
group by dept_id) s
where e.dept_id = s.dept_id
and e.salary > s.avgsal;
4.4 select之后
外连接的另一种实现方式,并且更为灵活
-- 列出员工及其所在部门的信息
select id,first_name,salary,(
select name from s_dept d where e.dept_id=d.id
) dname
from s_emp e;
-- 练习:列出所有和‘Mark‘在同一部门的员工的信息
select id,first_name,dept_id from s_emp
where dept_id=
(select dept_id from s_emp where first_name=‘Mark‘);
对象名_姓名缩写_座位号
xxxx_zsm_00
-----------------------------------------------------------------
5.表的操作
5.1 表的创建和删除
5.1.1 标识符的命名
1) 由a-z、A-Z、0-9、_、$、#构成
2)必须用字母开头
3) 不能和关键字重名
4) 不能和其他的数据库对象重名
5) 1-30位
5.1.2 创建表
-- 语法
create table 表名(
字段名 数据类型,
....
字段名 数据类型
);
create table testid_zsm_00(
id number,
name varchar2(20)
);
5.1.3 删除表
drop table 表名;
drop table testid_zsm_00;
5.2 数据操作语句(DML)
-- 创建一个测试表
三个字段:
编号 数字
名字 字符串
入职日期 日期
create table emp_zsm_00(
id number(7),
name varchar2(20),
start_date date
);
5.2.1 插入语句(insert) -- 一次一整行
1) 语法
insert into 表名[(字段列表)] values(值列表);
字段列表的数量和顺序和值列表的数量和顺序必须一致
2) 不省略字段列表
-- 写入全部字段
insert into emp_zsm_00(id,name,start_date)
values(1,‘test1‘,‘13-OCT-17‘);
commit;
-- 写入部分字段 ( 没有给值的字段必须允许为null )
insert into emp_zsm_00(id,name)
values(2,‘test2‘);
commit;
3) 省略字段列表(相当于给出了全部字段,并且顺序和表结构一致)
-- 给全部字段提供值
insert into emp_zsm_00 values(3,‘test3‘,sysdate);
commit;
insert into emp_zsm_00 values(4,‘test4‘,
to_date(‘2017-10-13‘,‘yyyy-mm-dd‘));
commit;
-- 给部分字段提供值
insert into emp_zsm_00 values(5,‘test5‘,null);
commit;
5.2.2 更新语句(update)
1) 语法
update 表名 set 字段 = 新值[,字段 = 新值,...]
[where 条件];
2) -- 更新 把入职日期改为‘2017-01-20‘
update emp_zsm_00 set start_date=‘12-JAN-17‘;
3) -- 把id=1的名字改为‘Ben‘,入职日期改为当前日期
update emp_zsm_00 set name=‘Ben‘,
start_date=sysdate where id=1;
commit;
5.2.3 删除语句(delete) -- 整行删除
1) 语法
delete [from] 表名 [where子句];
2) 删除表中全部数据
delete from emp_zsm_00;
rollback; -- 撤销没有提交的操作
3) 带where子句的删除
delete from emp_zsm_00 where name like ‘test%‘;
commit;
5.3 事务控制语句 (TCL)
select: 和事务无关
ddl: 隐式提交的事务
dml: 默认需要显式提交
5.3.1 事务控制语句的含义
commit; 确认事务(提交所有未提交的操作)
savepoint 保存点; 定义保存点
rollback; 回滚事务(撤销所有未提交的操作)
rollback to 保存点; 回顾到保存点的位置
5.3.2 事务的四大特性(ACID)
1) 原子性:事务中的语句是一个不可分割的整体
转账:
账户表:account
字段:id balance
A账户---> B账户 2000
update account set balance = balance - 2000
where id=‘A‘;
a
update account set balance = balance + 2000
where id=‘B‘;
b
if a&&b
commit;
else
rollback;
要么全部成功,要么全部失败
2)一致性
事务执行的结果必须是使数据库从一种一致性状态变为另一种一致性状态
3) 隔离性
一个事务对数据的改变,在提交之前,对于其他的事务是不可见的。
4) 持久性
事务一旦提交,对数据的改变就是永久的
5.3.3 部分成功 部分失败
-- 删除表中全部数据 属于ddl语句,不能撤销
truncate table emp_zsm_00;
insert into emp_zsm_00(id,name) values(1,‘test1‘);
savepoint a;
insert into emp_zsm_00(id,name) values(2,‘test2‘);
savepoint b;
insert into emp_zsm_00(id,name) values(3,‘test3‘);
savepoint c;
insert into emp_zsm_00(id,name) values(4,‘test4‘);
select * from emp_zsm_00;
rollback to b;
commit;
select * from emp_zsm_00;
----------------------------------------------------------------
练习:
1.使用select语句建表:
drop table emp_zsm_00;
create table emp_zsm_00 as select * from s_emp;
2.给所有‘Carmen‘的下属涨工资
1) 查询‘Carmen‘的编号
2) 根据‘Carmen‘的编号 查询其下属的编号
3) 根据员工编号 改工资
3.删除和‘Ben‘同部门的员工
--day04--
回顾:
1.sql99中的表连接
select 字段列表
from 左表 {[inner]|{left|right|full} [outer]} join 右表
on 关联条件;
2. 集合操作
union
union all
intersect
minus
3. 组函数和分组
3.1 组函数
count *
max
min
sum
avg
distinct
NULL
3.2 分组
group by
having
语法:
select
from
where
group by
having
order by
执行:
from
where
group by
having
select
order by
4. 子查询
where子句
select id,first_name,salary from s_emp
where salary>(
select salary froms _emp where first_name=‘Ben‘
);
select id,first_name from s_emp
where id in(
select distinct manager_id from s_emp
);
select id,first_name from s_emp
where id not in(
select distinct manager_id from s_emp
where manager_id is not null
);
select id,name from s_dept d
where exists(select * from s_emp e
where e.dept_id = d.id);
having子句
select dept_id,avg(salary) avgsal
from s_emp
group by dept_id
having avg(salary)>(
select avg(salary) from s_emp
);
from子句
select e.id,e.first_name,e.salary,s.avgsal
from s_emp e,
(select dept_id,avg(salary) avgsal from s_emp
group by dept_id) s
where e.dept_id = s.dept_id
and e.salary>s.avgsal;
select子句
select e.id,e.first_name,e.salary,(
select name from s_dept d where e.dept_id = d.id
) dname
from s_emp e;
5.表操作
5.1 创建表和删除表
create table 表名(
字段名 数据类型,
....
字段名 数据类型
);
drop table 表名;
5.2 数据操作语句
1)insert
insert into 表名[(字段列表)] values(值列表);
2) update
update 表名 set 字段=新值[,字段=新值,...]
[where子句];
3) delete
delete [from] 表名 [where子句];
commit;
truncate table 表名; -- 属于ddl语句,功能上等效于没有where子句的delete语句
5.3 事务控制语句
commit;
rollback;
savepoint 保存点;
rollback to 保存点;
事务的ACID特性
----------------------------------------------------------------
练习:
2.给所有‘Carmen‘的下属涨工资
1) 查询‘Carmen‘的编号
select id from emp_zsm_00
where first_name=‘Carmen‘;
2) 根据‘Carmen‘的编号 查询其下属的编号
select id from s_emp where manager_id = (
select id from emp_zsm_00
where first_name=‘Carmen‘
);
3) 根据员工编号 改工资
update emp_zsm_00 set salary = salary + 500
where id in(
select id from emp_zsm_00 where manager_id =
(
select id from emp_zsm_00
where first_name=‘Carmen‘
)
);
3.删除和‘Ben‘同部门的员工
delete from emp_zsm_00 where dept_id=
(select dept_id from emp_zsm_00
where first_name=‘Ben‘)
and first_name !=‘Ben‘;
-----------------------------------------------------------------
1. 约束 constraint
数据完整性:
实体完整性
域完整性
引用完整性
自定义完整性
1.1 约束的概念
对表中的字段添加的限制
1.2 约束的具体类型
主键约束:primary key
非空、唯一
一张表只能有一个主键
唯一约束: unique
不能重复
允许为null
一个表中可以有多个唯一
非空约束: not null
不允许为null
检查约束:check(检查约束表达式)
字段的值必须能够使检查约束表达式为真
外键约束: foreign key references
1.3 约束的实现方式
列级约束:定义完一个字段后,直接在后边添加约束
表级约束:定义完一个表的所有字段后,逗号隔开,
再添加约束
1.4 主键约束
1.4.1 列级实现:
1) 系统自动为约束命名
create table testcons_zsm_00(
id number(7) primary key,
name varchar2(20)
);
insert into testcons_zsm_00 values(1,‘test1‘);
/* 违反了唯一约束 */
insert into testcons_zsm_00 values(1,‘test2‘);
*
ERROR at line 1:
ORA-00001: unique constraint (OPENLAB.SYS_C0042650) violated
/* 主键字段的值不允许为null */
insert into testcons_zsm_00(name) values(‘test2‘);
*
ERROR at line 1:
ORA-01400: cannot insert NULL into ("OPENLAB"."TESTCONS_ZSM_00"."ID")
2) 手动为约束命名
表名_字段_约束类型 : testcons_id_pk
字段 数据类型 constraint 约束名 约束类型
drop table testcons_zsm_00;
create table testcons_zsm_00(
id number(7) constraint testcons_id_pk_zsm_00 primary key,
name varchar2(20)
);
insert into testcons_zsm_00 values(1,‘test1‘);
/* 违反了唯一约束 */
insert into testcons_zsm_00 values(1,‘test1‘);
*
ERROR at line 1:
ORA-00001: unique constraint (OPENLAB.TESTCONS_ID_PK_ZSM_00) violated
1.4.2 表级实现 复合主键(组合键)
drop table testcons_zsm_00;
create table testcons_zsm_00(
id number(7) primary key,
userid number(10) primary key,
name varchar2(20)
);
userid number(10) primary key,
*
ERROR at line 3:
ORA-02260: table can have only one primary key
create table testcons_zsm_00(
id number(7),
userid number(10),
name varchar2(20),
constraint testcons_id_userid_pk_zsm_00 primary key(id,userid)
);
insert into testcons_zsm_00 values(1,1,‘test1‘);
insert into testcons_zsm_00 values(1,2,‘test1‘);
insert into testcons_zsm_00 values(2,1,‘test1‘);
insert into testcons_zsm_00 values(2,2,‘test1‘);
/* 违反了唯一约束 */
insert into testcons_zsm_00 values(2,2,‘test1‘);
注意:不建议使用复合主键
1.5 唯一约束、非空约束和检查约束
1.5.1 列级实现
drop table testcons_zsm_00;
create table testcons_zsm_00(
id number primary key,
name varchar2(20) not null,
userid varchar2(18) unique,
sal number(11,2) check(sal>2000)
);
/* 不能向name字段插入null(非空约束的字段不允许为null) */
insert into testcons_zsm_00(id,userid,sal)
values(1,‘1234‘,2500);
insert into testcons_zsm_00(id,name,userid,sal)
values(1,‘name1‘,‘1234‘,2500);
/* 违反了唯一约束 */
insert into testcons_zsm_00(id,name,userid,sal)
values(2,‘name2‘,‘1234‘,2350);
/* 唯一约束的字段允许为null */
insert into testcons_zsm_00(id,name,sal)
values(3,‘name3‘,2350);
/* 唯一约束的字段 可以有多个null值 */
insert into testcons_zsm_00(id,name,sal)
values(4,‘name4‘,2350);
/* 违反了检查约束 */
insert into testcons_zsm_00(id,name,sal)
values(5,‘name5‘,1350);
*
ERROR at line 1:
ORA-02290: check constraint (OPENLAB.SYS_C0042698) violated
1.5.2 表级实现
drop table testcons_zsm_00;
/* 一个表中有两个唯一约束 */
create table testcons_zsm_00(
id number unique,
name varchar2(20) not null,
userid varchar2(18) unique,
sal number(11,2) check(sal>2000)
);
insert into testcons_zsm_00 values(1,‘test1‘,‘12‘,3000);
/* 违反了id字段的唯一约束 */
insert into testcons_zsm_00 values(1,‘test1‘,‘123‘,3000);
/* 违反了userid的唯一约束 */
insert into testcons_zsm_00 values(2,‘test1‘,‘12‘,3000);
drop table testcons_zsm_00;
/* id和userid组合作为唯一键 */
create table testcons_zsm_00(
id number ,
name varchar2(20) not null,
userid varchar2(18),
sal number(11,2),
unique(id,userid),
check(sal>2000)
);
1.5.3 从业务层面,没有多个字段联合非空的需要,所有Oracle没有提供非空约束的表级实现
1.6 外键约束
一个表中某字段的值,受另一张表中某个字段的限制
主表(父表):提供数据的表
从表(子表):外键所在的表
(引用主表中唯一性字段(主键、唯一)的值)
外键的值只能是主表中对应字段的值或者为null
外键约束的语法:
references 主表(字段)
constraint 约束名 references 主表(字段)
1.6.1 创建表
一般先创建主表,再创建从表(除非建表时也不添加外键约束)
/* 创建主表 */
create table parent_zsm_00(
id number(7) primary key,
name varchar2(25) not null
);
/* 创建子表 */
create table child_zsm_00(
id number(7) primary key,
c_name varchar2(20) not null,
p_id number(7) references parent_zsm_00(id)
);
1.6.2 数据操作语句(dml)
对于有外键约束的表,执行dml操作时,一定要确保子表中
外键的值不能孤立(在主表中可以找到)
1) insert
insert into parent_zsm_00 values(1,‘Admin‘);
commit;
insert into child_zsm_00 values(1001,‘test1‘,1);
insert into child_zsm_00 values(1002,‘test2‘,null);
commit;
2) update
/* 违反了完整性约束-- 添加或更新子表数据时,主表中不存在该值 */
update child_zsm_00 set p_id=2 where id=1002;
*
ERROR at line 1:
ORA-02291: integrity constraint (OPENLAB.SYS_C0042774) violated - parent not found
/* 违反了完整性约束-- 更新或删除主表数据时,数据被子表引用 */
update parent_zsm_00 set id=2 where id=1;
*
ERROR at line 1:
ORA-02292: integrity constraint (SYSTEM.SYS_C0042798) violated - child record
found
3) delete
delete from parent_zsm_00 where id=1;
1.6.3 删除表
一般先删除子表,再删除主表
drop table child_zsm_00;
drop table parent_zsm_00;
-- 对表的级联删除(先解除外键约束,然后再删除表)
drop table 表名 cascade constraits;
drop table parent_zsm_00 cascade constraints;
drop table child_zsm_00 cascade constraints;
1.6.4 表中数据的级联删除和级联置空
级联删除:on delete cascade
级联置空:on delete set null
/* 创建主表 */
create table parent_zsm_00(
id number(7) primary key,
name varchar2(25) not null
);
/* 创建子表 */
create table child_zsm_00(
id number(7) primary key,
c_name varchar2(20) not null,
p_id number(7),
constraint parent_id_child_pid_fk_zsm_00 foreign key(p_id) references parent_zsm_00(id) on delete cascade
);
insert into parent_zsm_00 values(1,‘Admin‘);
insert into parent_zsm_00 values(2,‘Test‘);
commit;
insert into child_zsm_00 values(1001,‘test1‘,1);
insert into child_zsm_00 values(1002,‘test2‘,1);
insert into child_zsm_00 values(1003,‘test3‘,2);
insert into child_zsm_00 values(1004,‘test4‘,2);
commit;
delete from parent_zsm_00 where id=1;
commit;
2. 其他的数据库对象和分页
2.1 其他的数据库对象
2.1.1 序列 sequence
1) 作用
给主键产生不重复的数字
2) 创建序列
create sequence 序列名;
-- 创建一个测试表
create table testseq_zsm_00(
id number(10) primary key,
name varchar2(20)
);
-- 创建序列
create sequence testseq_id_zsm_00;
3) 使用序列
nextval:产生一个新的序列值
currval: 当前的序列值
序列名.nextval
序列名.currval
第一个使用一个序列时,应该首先调用nextval获取第一个值
insert into testseq_zsm_00 values(
testseq_id_zsm_00.nextval,
‘test‘||testseq_id_zsm_00.currval);
commit;
相关数字字典:user_sequences
4) 删除序列
drop sequence 序列名;
drop sequence testseq_id_zsm_00;
2.1.2 索引 index
1) 作用
提高查询的效率
用增删改的时间和和索引页占用的空间获取查询效率的提升
2) 系统会自动为主键字段添加索引
s_index:id(pk) name
set timing on -- 显式语句执行的时间
select id,name from s_index where id=1000000;
select id,name from s_index
where name=‘test1000000‘;
-- 创建测试表
create table testindex(
id number(10) primary key,
name varchar2(20) not null
);
-- 创建一个序列
create sequence testindex_id;
begin
for var_i in 1..5000000 loop
insert into testindex values(testindex_id.nextval,
‘test‘||testindex_id.currval);
commit;
end loop;
end;
3) 手动创建索引
语法:
create index 索引名 on 表名(字段);
4) 删除索引
drop index 索引名;
相关的数据字典:user_indexes;
2.1.3 视图 view
1) 作用
简化查询操作
数据安全
本质上视图就是一条select语句
2) 创建视图
语法:
create [or replace] view 视图名[(别名列表)]
as select语句
[with check option]
[with read only];
3)创建一个视图 (基于emp_zsm_00)
create or replace view vw_emp_zsm_00
as
select id,last_name,first_name,salary
from emp_zsm_00
where dept_id in(31,32,33,34,35);
select * from vw_emp_zsm_00;
insert into vw_emp_zsm_00 values(
30,‘LName‘,‘FName‘,2500);
commit;
-- 创建只读视图
create or replace view vw_emp_zsm_00
as
select id,last_name,first_name,salary
from emp_zsm_00
where dept_id in(31,32,33,34,35)
with read only;
/* 只读视图 不能执行dml操作 */
insert into vw_emp_zsm_00 values(
31,‘LName‘,‘FName‘,2500);
4)删除视图
drop view 视图名;
drop view vw_emp_zsm_00;
相关的数字字典: user_views
2.2 分页
sqlserver: top
mysql: limit
oracle: rownum
rownum:伪列
-- 显示s_emp表中第一页(每页5行)
select rownum,first_name,salary from s_emp
where rownum<6;
-- 显示s_emp表中第二页(每页5行)
/* rownum不能使用> */
select rownum,first_name,salary from s_emp
where rownum>=6 and rownum<11;
select * from
(select rownum rn,id,first_name,salary from s_emp
where rownum<11)
where rn>=6;
-- 按照工资降序排序,显示第二页
select * from
(
select rownum rn,id,first_name,salary from
(
select id,first_name,salary from s_emp
order by salary desc
)
where rownum<11
)
where rn>=6;
----------------------------------------------------------------
练习:
1. 创建一张表
编号 数字 主键
名字 字符串 非空
入职日期 日期
工资 数字 不能低于700
2.把s_emp表中的数据导入新建表中
insert into 表名(字段列表) select语句;
3.在新建表的名字字段上创建索引
4.在新建表上创建视图,并测试对视图执行操作
--day05--
PLSQL
1.常用的访问数据库的相关技术
1) plsql 过程化的sql
2) proc/c++ 在c/c++语言中访问oracle数据库的技术
3) ado/odbc vc中访问数据库的技术
4) oci oracle底层提供的客户端的连接接口
5) sql j/jdbc java访问数据库的技术
2.PLSQL
2.1 概念
plsql(procedured language/sql)是在标准sql的基础上增加了过程化的处理形成的语言
oracle客户端工具访问oracle服务器的操作语言
是对sql的扩充
2.2 特点
结构化模块化编程
良好的可移植性
良好的可维护性
提升系统性能
不便于向异构数据库移植
3.SQL语言的特点
机器语言 汇编语言 高级语言 结构化语言
只管做什么 不管怎么做
没有过程和控制结构
没有算法描述能力
4.plsql扩充了sql
1) 变量和数据类型
2) 控制语句
3) 过程和函数
4) 对象类型和方法
5.plsql的程序框架
declare
/* 声明区 声明变量、定义类型等 */
-- 单行注释 ‘--‘
-- 多行注释 /*内 容*/
-- 如果没有需要声明、定义的内容,声明区可以省略
begin
/* 执行区 执行sql或plsql语句 */
exception
/* 异常处理区 处理异常的区域 */
/* 可省略 */
end;
/ -- 表示plsql代码结束 必须独立占一行
6.plsql的开发工具
sqlplus 命令提示符的工具
plsqldeveloper 图形化的开发工具
begin
dbms_output.put_line(‘Hello World!‘);
end;
/
-- 打开输出功能
set serveroutput on
7.标识符
7.1 作用
给变量、数据类型、游标、过程、函数、触发器、包等命名
7.2 使用变量
declare
变量名 数据类型;
变量名 数据类型 := 值;
begin
变量名 := 值;
-- 声明两个变量,赋值并输出
declare
var_id number;
var_name varchar2(20):=‘test‘;
begin
var_id:=1;
dbms_output.put_line(var_id||‘,‘||var_name);
end;
/
8.变量和数据类型
8.1 数据类型
1) 标量类型
number binary_integer 数字类型
char varchar2 字符串类型
date 日期类型
boolean 布尔类型
2) 复合类型
record
table
3) 参考类型 ref
ref cursor(参考游标)
4) 大类型
BLOB 0-4g
CLOB 0-4g
B FILE
8.2 变量的修饰符
变量名 constant 数据类型;
变量名 数据类型 not null;
declare
-- (1)
-- var_id constant number;
var_id constant number:=1;
-- (3)
-- var_name varchar(20) not null;
var_name varchar(20) not null:=‘1‘;
begin
-- (2)
-- var_id:=1;
var_name:=‘test‘;
dbms_output.put_line(var_id);
dbms_output.put_line(var_name);
end;
/
(1) 用constant修饰的变量必须初始化
(2) 用constant修饰的变量不允许赋值
(3) 用not null修饰的变量必须初始化
任何类型的变量,在赋值前 初值都是null
8.3 使用binary_integer和boolean定义变量
true false null
declare
var_id binary_integer:=100;
var_flag boolean;
begin
var_flag:=true;
if var_flag then
dbms_output.put_line(var_id);
end if;
end;
/
8.4 声明两个变量,类型分别和s_emp表中id、first_name相同,用来接收id=1的员工的id和first_name,并输出
declare
var_id number(7);
var_name varchar2(25);
begin
var_id:=1;
var_name:=‘Carmen‘;
dbms_output.put_line(var_id||‘,‘||var_name);
end;
/
获取表中字段的数据类型:表名.字段名%type
使用select语句给变量赋值:
select 字段列表 into 变量列表 from 表名 where 条件;
注意:字段列表和变量列表的顺序、数量和数据类型要一致
select语句有且只有一行结果
declare
var_id s_emp.id%type;
var_name s_emp.first_name%type;
begin
select id,first_name into var_id,var_name from s_emp
where id=1;
dbms_output.put_line(var_id||‘,‘||var_name);
end;
/
8.5 record类型 相当于C语言中的结构体
8.5.1 定义record类型
type 记录类型名 is record(
字段名 类型,
...
字段名 类型
);
8.5.2 定义record类型,有三个字段,类型分别和s_emp表中id,first_name,salary相同。声明该类型变量,用于接收id=2的员工的id,first_name和salary,并输出。
declare
-- 定义record类型
type emprecord is record(
id s_emp.id%type,
name s_emp.first_name%type,
sal number(11,2)
);
-- 声明record类型变量
var_emp emprecord;
var_emp2 emprecord;
begin
select id,first_name,salary into var_emp from s_emp
where id=2;
dbms_output.put_line(var_emp.id||‘,‘||var_emp.name||‘,‘
||var_emp.sal);
var_emp2:=var_emp;
dbms_output.put_line(var_emp2.id||‘,‘
||var_emp2.name||‘,‘
||var_emp2.sal);
end;
/
-- 查询的字段的数量少于record类型的字段的数量
declare
-- 定义record类型
type emprecord is record(
id s_emp.id%type,
name s_emp.first_name%type,
sal number(11,2)
);
-- 声明record类型变量
var_emp emprecord;
begin
select id,first_name into var_emp.id,var_emp.name
from s_emp where id=2;
dbms_output.put_line(var_emp.id||‘,‘||var_emp.name||‘,‘
||var_emp.sal);
end;
/
-- 接收表中的全部字段,record类型?
8.5.3 用于接收表中整行数据的类型
表名%rowtype
-- 查询s_emp表中 id=3的员工的全部信息,保存在变量中并输出
declare
var_emp s_emp%rowtype;
begin
select * into var_emp from s_emp where id=3;
dbms_output.put_line(var_emp.id||‘,‘||
var_emp.first_name||‘,‘||
var_emp.title);
end;
/
8.6 table类型 类似于C语言中的数组
8.6.1 定义table类型的语法
type table类型 is table of 元素的数据类型
index by binary_integer;
8.6.2 定义一个table类型,声明变量,保存多个数字
declare
/* 定义table类型 */
type numstable is table of number
index by binary_integer;
/* 声明table类型的变量 */
var_nums numstable;
begin
var_nums(3):=100;
var_nums(7):=200;
var_nums(1):=300;
dbms_output.put_line(var_nums(3));
-- dbms_output.put_line(var_nums(2));
end;
8.6.3 下标连续时,table类型变量的访问
declare
/* 定义table类型 */
type numstable is table of number
index by binary_integer;
/* 声明table类型的变量 */
var_nums numstable;
/* 声明变量 表示下标 */
var_i binary_integer;
begin
var_nums(3):=100;
var_nums(5):=200;
var_nums(4):=300;
var_i:=3;
dbms_output.put_line(var_nums(var_i));
var_i:=var_i + 1;
dbms_output.put_line(var_nums(var_i));
var_i:=var_i + 1;
dbms_output.put_line(var_nums(var_i));
-- var_i:=var_i + 1;
end;
/
8.6.4 下标不连续时,对table类型变量的遍历
first() -- 获取第一个元素的下标
next(n) -- 获取下标为n的元素的下一个元素的下标
last() -- 获取最后一个元素的下标
declare
/* 定义table类型 */
type numstable is table of number
index by binary_integer;
/* 声明table类型的变量 */
var_nums numstable;
/* 声明变量 表示下标 */
var_i binary_integer;
begin
var_nums(3):=100;
var_nums(7):=200;
var_nums(4):=300;
var_i:=var_nums.first();
dbms_output.put_line(var_nums(var_i));
var_i:=var_nums.next(var_i);
dbms_output.put_line(var_nums(var_i));
var_i:=var_nums.next(var_i);
dbms_output.put_line(var_nums(var_i));
-- var_i:=var_nums.next(var_i);
end;
8.6.5 定义table类型变量,保存s_emp表中id为1,3,7的员工的信息,并输出
declare
/* 定义table类型 */
type empstable is table of s_emp%rowtype
index by binary_integer;
/* 声明table类型变量 */
var_emps empstable;
/* 声明变量 保存下标 */
var_i binary_integer;
begin
select * into var_emps(1) from s_emp where id=1;
select * into var_emps(3) from s_emp where id=3;
select * into var_emps(7) from s_emp where id=7;
var_i:=var_emps.first();
dbms_output.put_line(var_emps(var_i).id||‘,‘||
var_emps(var_i).first_name||‘,‘||
var_emps(var_i).salary);
var_i:=var_emps.next(var_i);
dbms_output.put_line(var_emps(var_i).id||‘,‘||
var_emps(var_i).first_name||‘,‘||
var_emps(var_i).salary);
var_i:=var_emps.next(var_i);
dbms_output.put_line(var_emps(var_i).id||‘,‘||
var_emps(var_i).first_name||‘,‘||
var_emps(var_i).salary);
var_i:=var_emps.next(var_i);
end;
8.6. 变量的作用域
declare
-- 全局
var_a number:=1;
begin
declare
-- 局部
var_b number:=2;
begin
dbms_output.put_line(var_a);
dbms_output.put_line(var_b);
end;
dbms_output.put_line(var_a);
-- dbms_output.put_line(var_b);
end;
局部既可以访问局部变量,也可以访问全局变量
全局 只可以访问全局变量
局部变量和全局变量重名时,局部变量会覆盖同名的全局变量
-- 局部访问同名的全局变量:
使用标签
定义标签: <<标签名>>
使用标签: 标签名.变量名
<<g>>
declare
-- 全局
var_a number:=1;
begin
declare
-- 局部
var_a number:=2;
begin
dbms_output.put_line(g.var_a);
end;
dbms_output.put_line(var_a);
end;
9. 控制语句
9.1 if语句
9.1.1 语法
1) 简单if语句
if 条件 then
操作
end if;
2) if...else语句
if 条件 then
操作1
else
操作2
end if;
3) 多分支if
if 条件1 then
操作1
elsif 条件2 then
操作2
....
else
操作n
end if;
4) 嵌入if
if 条件1 then
if 条件2 then
操作1
else
操作2
end if;
else
if 条件3 then
操作3
else
操作4
end if;
end if;
9.1.2 练习:声明三个数字类型变量并赋值,输出最大值
-- 方法一:
declare
var_a number:=1;
var_b number:=200;
var_c number:=100;
begin
if var_a > var_b then
if var_a > var_c then
dbms_output.put_line(var_a);
else
dbms_output.put_line(var_c);
end if;
else
if var_b > var_c then
dbms_output.put_line(var_b);
else
dbms_output.put_line(var_c);
end if;
end if;
end;
/
-- 方法二:
declare
var_a number:=&var_a;// 后面这‘&‘是输入相当于c语言里的scanf
var_b number:=&var_b;
var_c number:=&var_c;
var_temp number;
begin
var_temp:=var_a;
if var_b > var_temp then
var_temp:=var_b;
end if;
if var_c >var_temp then
var_temp:=var_c;
end if;
dbms_output.put_line(var_temp);
end;
9.1.3 NULL的运算特点
declare
var_a number;
var_b number;
begin
if var_a > var_b then
dbms_output.put_line(‘a>b‘);
elsif var_a < var_b then
dbms_output.put_line(‘a<b‘);
elsif var_a = var_b then
dbms_output.put_line(‘a=b‘);
elsif var_a is null and var_b is null then
dbms_output.put_line(
‘var_a is null and var_b is null‘);
end if;
end;
/
9.2 循环语句
循环变量初始化 循环条件 循环操作 条件的更新
9.2.1 简单循环
1) 语法
loop
-- 循环操作
end loop;
2) 结束循环的方式
-- 方式一:
if 退出循环的条件 then
exit;
end if;
-- 方式二:
exit when 退出循环的条件;
3) 使用简单循环输出1..10
declare
var_i number:=1;
begin
loop
dbms_output.put_line(var_i);
exit when var_i=10;
var_i:=var_i+1;
end loop;
end;
/
-- 使用if退出循环
declare
var_i number:=10;
begin
loop
dbms_output.put_line(var_i);
if var_i=1 then
dbms_output.put_line(‘loop over!‘);
exit;
end if;
var_i:=var_i-1;
end loop;
end;
/
9.2.2 while循环
1) 语法
while 条件 loop
-- 循环操作
end loop;
2) 使用while循环输出1..10
declare
var_i number:=1;
begin
while var_i<=10 loop
dbms_output.put_line(var_i);
var_i:=var_i+1;
end loop;
end;
-- 练习:从1..100累加,输出和大于2000时对应的数字
declare
var_i number:=1;
var_sum number:=0;
begin
while var_i<=100 loop
var_sum:=var_sum + var_i;
if var_sum >=2000 then
dbms_output.put_line(var_i);
exit;
end if;
var_i := var_i + 1;
end loop;
end;
9.2.3 for 循环
智能循环
1) 语法
for 循环变量 in 区间 loop
-- 循环操作
end loop;
2) 使用for 循环输出1..10
begin
for var_i in 1..10 loop
dbms_output.put_line(var_i);
end loop;
end;
-- 练习 使用for循环输出10..1
begin
for var_i in 10..1 loop
dbms_output.put_line(var_i);
end loop;
end;
-- 上边代码循环一次也不执行
使用 reverse 实现倒序输出
begin
for var_i in reverse 1..10 loop
dbms_output.put_line(var_i);
end loop;
end;
-- 使用退出循环的方式 提前结束循环
begin
for var_i in 1..10 loop
dbms_output.put_line(var_i);
if var_i=5 then
-- var_i:=11; 错误
end if;
end loop;
end;
a.循环变量不需要声明
b.区间必须是从小到大 使用reverse实现反转循环
c.循环变量不允许赋值
9.3 跳转语句 goto
9.3.1 语法
<<标签名>>
-- 这里必须有语句
NULL; -- 空语句
goto 标签名;
9.3.2 使用goto退出嵌套循环
begin
for var_i in 1..3 loop
for var_j in 1..5 loop
dbms_output.put_line(var_j);
if var_j=3 then
goto outer;
end if;
end loop;
end loop;
<<outer>>
NULL;
end;
9.3.3 使用if..exit方式退出循环
begin
<<outer>> //提到前面来比较好,能够一眼被一眼的看出来
for var_i in 1..3 loop
for var_j in 1..5 loop
dbms_output.put_line(var_j);
if var_j=3 then
exit outer;
end if;
end loop;
end loop;
dbms_output.put_line(‘loop over‘);
end;
10. plsql中使用sql语句
1) select语句
select语句要和into 配合使用
select 字段列表 into 变量列表 from 表名 where 条件;
2) dml语句
tcl语句
可以直接在plsql中使用
3) ddl语句
不能直接在plsql中使用 需要用动态sql实现
11.动态sql
11.1 概念
把一条字符串对应的sql语句,当成真正的sql语句去执行
11.2 案例: 创建一张表
/* 错误:ddl语句不能直接在plsql中使用 */
begin
create table testdsql_zsm_00(id number);
end;
-- 在plsql中可以使用函数
declare
sqlstr varchar2(100);
begin
sqlstr:=‘create table testdsql_zsm_00(id number)‘;
sqlstr:=substr(sqlstr,1,length(sqlstr)-1)
||‘,name varchar2(20))‘;
-- dbms_output.put_line(sqlstr);
execute immediate sqlstr;
end;
11.3 DML语句的动态sql
1) 直接在plsql中使用dml和tcl语句
begin
insert into testdsql_zsm_00 values(1,‘test1‘);
commit;
end;
2) 常规字符串的拼接
declare
sqlstr varchar2(100);
begin
sqlstr:= ‘insert into testdsql_zsm_00 values(2,‘‘test2‘‘)‘;
execute immediate sqlstr;
commit;
end;
3)带有变量的字符串的拼接
declare
var_id number:=3;
var_name varchar2(20):=‘test3‘;
sqlstr varchar2(100);
begin
sqlstr:=‘insert into testdsql_zsm_00 values(‘||var_id||
‘,‘‘‘||var_name||‘‘‘)‘;
-- dbms_output.put_line(sqlstr);
execute immediate sqlstr;
commit;
end;
4) 使用占位符 配合using解决字符串拼接的问题
占位符: :占位符名
execute immediate sqlstr using 变量列表;
declare
var_id number:=4;
var_name varchar2(20):=‘test4‘;
sqlstr varchar2(100);
begin
sqlstr:=‘insert into testdsql_zsm_00 values(:b0,:b1)‘;
execute immediate sqlstr using var_id,var_name;
commit;
end;
11.4 select语句动态sql
必须是普通的select语句(不带into)
select语句有且只有一行结果
declare
sqlstr varchar2(100);
var_name s_emp.first_name%type;
begin
sqlstr:=‘select first_name from s_emp where id=1‘;
execute immediate sqlstr into var_name;
dbms_output.put_line(var_name);
end;
-----------------------------------------------------------------
总结:
1.变量和类型
定义和使用record和table类型
type 类型名 is record(
字段 类型,
....
字段 类型
);
type 类型名 is table of 元素类型 index by binary_integer;
表名.字段%type
表名%rowtype
2.控制语句
分支:if
循环:简单循环、while循环、for循环
跳转语句:goto
结束循环的两种方式:
exit when 退出循环的条件;
if 退出循环的条件 then
exit;
end if;
3.动态sql
sqlstr varchar2(200):=‘‘;
execute immediate sqlstr;
‘insert into 表名 values(:b0,:b1,....)‘
execute immediate sqlstr using 变量1,变量2,....;
-----------------------------------------------------------------
练习:
1.定义record类型,声明变量,保存s_dept表中id=31的部门的
信息
2. 使用动态sql语句,删除testdsql_zsm_00表中指定id 的信息
--day06--
回顾:
1.定义record类型,声明变量,保存s_dept表中id=31的部门的
信息
declare
/* 定义record类型 */
type deptrecord is record(
id number,
name s_dept.name%type,
r_id number
);
/* 声明变量 */
var_dept deptrecord;
var_id number:=&id;
begin
select * into var_dept from s_dept where id=var_id;
dbms_output.put_line(var_dept.id||‘,‘||
var_dept.name||‘,‘||
var_dept.r_id);
end;
2. 使用动态sql语句,删除testdsql_zsm_00表中指定id 的信息
declare
sqlstr varchar2(100);
var_id number:=1;
begin
-- sqlstr:=‘delete from testdsql_zsm_00 where id=‘
-- ||var_id;
sqlstr:=‘delete from testdsql_zsm_00 where id=:b0‘;
dbms_output.put_line(sqlstr);
execute immediate sqlstr using var_id;
commit;
end;
-----------------------------------------------------------------
1.游标 cursor
1.1 概念
游标是映射在结果集中一行数据的位置指针或位置实体。
有了游标,用户就可以访问多行结果集中的任何一行数据。
把游标放到结果集中某行数据后,即可以对该行数据做操作,
比如把数据提取出来。
1.2 作用
处理多行结果集
1.3 游标的使用步骤
1) 声明游标
声明游标名及其对应的select语句
在声明区声明
可以使用 游标名%rowtype 声明记录类型变量
语法:
cursor 游标名 is select语句;
2) 打开游标
执行select语句,并把结果集保存在游标对应的工作区中
游标指向结果集的第一行数据
open 游标名;
3) 提取数据
把游标指向的这一行数据提取出来,保存在变量中
游标自动向下移动一行
语法:
fetch 游标名 into 变量;
4) 关闭游标
当提取和处理数据结束后,应及时关闭游标以释放游标所占用的系统资源。
关闭的游标可以使用open再次打开
语法:
close 游标名;
1.4 使用游标 读取s_emp表中的全部数据
declare
/* 声明游标 */
cursor empcursor is select * from s_emp;
/* 声明变量 */
var_emp empcursor%rowtype;
begin
/* 打开游标 */
open empcursor;
/* 提取数据 */
fetch empcursor into var_emp;
dbms_output.put_line(var_emp.id||‘,‘||
var_emp.first_name||‘,‘||
var_emp.salary);
fetch empcursor into var_emp;
dbms_output.put_line(var_emp.id||‘,‘||
var_emp.first_name||‘,‘||
var_emp.salary);
fetch empcursor into var_emp;
dbms_output.put_line(var_emp.id||‘,‘||
var_emp.first_name||‘,‘||
var_emp.salary);
/* 关闭游标 */
close empcursor;
end;
1.5 游标的属性
游标名%属性
found 当提取数据时,如果提取到了新数据,则为真
没有提取到新数据,则为假
如果在open之前,返回非法游标
如果在fetch之前,返回null
notfound 当提取数据时,如果提取到了新数据,则为假
没有提取到新数据,则为真
如果在open之前,返回非法游标
如果在fetch之前,返回null
isopen 游标是否处理打开状态
打开时返回真,没有打开返回假
已经打开的游标不能再次打开
已经关闭的游标不能再次关闭
rowcount 游标的偏移量
如果没有open,返回非法游标
1.6 使用简单循环 配合 notfound 属性 遍历游标
declare
/* 声明游标 */
cursor empcursor is select * from s_emp;
/* 声明变量 */
var_emp empcursor%rowtype;
begin
/* 打开游标 */
open empcursor;
/* 循环提取数据 */
loop
fetch empcursor into var_emp;
exit when empcursor%notfound;
dbms_output.put_line(var_emp.id||‘,‘||
var_emp.first_name||‘,‘||
var_emp.salary);
end loop;
/* 关闭游标 */
close empcursor;
end;
1.7 使用while循环 配合found属性 遍历游标
declare
/* 声明游标 */
cursor empcursor is select * from s_emp;
/* 声明变量 */
var_emp empcursor%rowtype;
begin
/* 打开游标 */
open empcursor;
/* 循环提取数据 */
fetch empcursor into var_emp;
while empcursor%found loop
dbms_output.put_line(var_emp.id||‘,‘||
var_emp.first_name||‘,‘||
var_emp.salary);
fetch empcursor into var_emp;
end loop;
/* 关闭游标 */
close empcursor;
end;
1.8 使用for循环遍历游标
智能循环(自动打开游标、提取数据、关闭游标)
declare
cursor empcursor is select * from s_emp;
begin
for var_emp in empcursor loop
dbms_output.put_line(var_emp.id||‘,‘||
var_emp.first_name||‘,‘||
var_emp.salary);
end loop;
end;
1.9 带参游标
参数的数据类型不能包含长度或精度的修饰,但是可以使用%type
声明:
cursor 游标名(形参列表) is select语句 where ....;
传参:在打开游标时传递实参
open 游标名(实参列表);
declare
cursor empcursor(var_id number)
is select * from s_emp where id>var_id;
begin
for var_emp in empcursor(10) loop
dbms_output.put_line(var_emp.id||‘,‘||
var_emp.first_name||‘,‘||
var_emp.salary);
end loop;
end;
1.10 参考游标 ref cursor
动态sql + 游标
参考游标的使用步骤:
sqlstr := ‘select * from s_emp‘;
1) 定义一个参考游标类型
type 参考游标类型名 is ref cursor;
2) 声明参考游标类型变量
var_empcursor 参考游标类型名;
3) 把动态sql语句和参考游标变量结合
open var_empcursor for sqlstr;
......
案例:使用参考游标 遍历s_emp表中的全部数据
declare
sqlstr varchar2(100);
type emprefcursor is ref cursor;
var_empcursor emprefcursor;
var_emp s_emp%rowtype;
begin
sqlstr:=‘select * from s_emp‘;
open var_empcursor for sqlstr;
loop
fetch var_empcursor into var_emp;
exit when var_empcursor%notfound;
dbms_output.put_line(var_emp.id||‘,‘||
var_emp.first_name||‘,‘||
var_emp.salary);
end loop;
close var_empcursor;
end;
带有占位符的sql语句
declare
sqlstr varchar2(100);
type emprefcursor is ref cursor;
var_empcursor emprefcursor;
var_emp s_emp%rowtype;
begin
sqlstr:=‘select * from s_emp where id>:b0‘;
-- using后可以使用变量,也可以使用值。一般用变量
open var_empcursor for sqlstr using 10;
loop
fetch var_empcursor into var_emp;
exit when var_empcursor%notfound;
dbms_output.put_line(var_emp.id||‘,‘||
var_emp.first_name||‘,‘||
var_emp.salary);
end loop;
close var_empcursor;
end;
2.PLSQL中的异常
2.1 系统预定义异常
Oracle系统自身为用户提供的、可以在plsql中使用的预定义异常,用于检查用户代码失败的一般原因。
系统预定义异常由系统定义和引发
用户只需要根据名字捕获和处理异常
案例:
declare
var_name varchar2(25);
var_id number:=&id;
begin
select first_name into var_name from s_emp
where id = var_id;
select first_name into var_name from s_emp
where first_name like ‘M%‘;
dbms_output.put_line(var_name);
exception
when no_data_found then
dbms_output.put_line(‘no emp‘);
when others then
dbms_output.put_line(sqlcode||‘.....‘||sqlerrm);
end;
常用的异常:
no_data_found: select..into语句没有返回行
too_many_rows: select..into语句返回多于一行的结果集
invalid_cursor: 非法游标
cursor_already_open: 游标已打开
dup_val_on_index: 唯一索引对应的列上有重复值
zero_divide: 除数为0
案例:处理多个异常
declare
var_name varchar2(25);
var_id number:=&id;
begin
select first_name into var_name from s_emp
where id = var_id;
select first_name into var_name from s_emp
where first_name like ‘M%‘;
dbms_output.put_line(var_name);
exception
when no_data_found then
dbms_output.put_line(‘no emp‘);
when too_many_rows then
dbms_output.put_line(‘ too many rows‘);
when others then
dbms_output.put_line(sqlcode||‘.....‘||sqlerrm);
end;
2.2 自定义异常
2.2.1 定义异常的步骤:
1) 定义异常
异常名 exception;
2) 根据条件引发异常
if 引发异常的条件 then
raise 异常名;
end if;
3) 捕获和处理异常
when 异常名 then
处理异常
2.2.2 案例:更新员工表中指定员工的工资,员工不存在时提示异常
declare
var_id s_emp.id%type:=&id;
/* 定义异常 */
no_result exception;
begin
update s_emp set salary=salary+500 where id=var_id;
if sql%notfound then
raise no_result;
end if;
commit;
exception
when no_result then
dbms_output.put_line(‘no result‘);
end;
隐式游标:在执行一个sql语句时,oralce会自动创建一个隐式游标。这个游标是内存中为处理该条sql语句的工作区。
隐式游标只要用于处理数据操作语句(insert、delete、update)的执行结果。
隐式游标也有属性,使用属性时需要用到游标名---sql
3. 存储过程 procedure
3.1 匿名块和有名块
匿名块:
匿名块不保存在与数据库中
每次使用都要进行编译
不能在其他块调用
有名块:
可以存储在数据库中
可以在任何需要的地方调用
有名块包括:procedure function package trigger
存储过程 函数 包 触发器
3.2 存储过程的创建
语法:
create [or replace] procedure 过程名[(参数列表)]
is|as
-- 临时变量
begin
exception
end;
3.3 无参的过程的创建和调用
1)创建:输出两数中的较大值
create or replace procedure getmax_zsm_00
is
var_a number:=10;
var_b number:=100;
begin
if var_a > var_b then
dbms_output.put_line(var_a);
else
dbms_output.put_line(var_b);
end if;
end;
2) 调用无参的存储过程
begin
getmax_zsm_00;
end;
3.4 带参的存储过程
3.4.1 使用的注意事项
1) 参数的数据类型不能包含长度或精度的修饰
2) 参数可以有多种模式,并且可以有默认值
参数名 {[in]|out|in out} 类型[{:=| default } 值]
参数的模式:
in 输入参数 负责向过程传入值 默认模式
实参是值、赋值后的变量
在过程内不能赋值
out 输出参数 负责传出值
实参必须是变量,不必赋值
在过程内必须赋值
in out 输入输出参数 既负责传入,又负责传出
实参是赋值后的变量
在过程内可以赋值
只有in模式的参数才可以有默认值
3.4.2 案例:创建一个带参的存储过程,传入两个数字,输出最大值
create or replace procedure getmax_zsm_00(
var_a in number:=10,var_b number)
is
begin
if var_a > var_b then
dbms_output.put_line(var_a);
else
dbms_output.put_line(var_b);
end if;
end;
3.4.3 带参的存储过程的调用
declare
var_x number:=123;
var_y number:=12;
begin
getmax_zsm_00(1,10);
getmax_zsm_00(var_x,100);
getmax_zsm_00(var_x,var_y);
-- getmax_zsm_00(1);
end;
3.4.4 参数赋值方式
1) 按照位置赋值
2) 按照名字赋值
参数名 => 值
declare
var_x number:=123;
var_y number:=12;
begin
getmax_zsm_00(1,10);
getmax_zsm_00(var_x,100);
getmax_zsm_00(var_b=>10,var_a=>200);
getmax_zsm_00(var_b=>1);
end;
3.4.5 练习:创建一个存储过程,传入两个数字,输出最大值,同时把两数之和保存在第二个变量,并测试
create or replace procedure getmax_zsm_00(
var_a in number,var_b in out number)
is
begin
if var_a > var_b then
dbms_output.put_line(var_a);
else
dbms_output.put_line(var_b);
end if;
var_b:=var_a + var_b;
end;
-- 调用
declare
var_x number:=100;
begin
getmax_zsm_00(10,var_x);
dbms_output.put_line(var_x);
end;
3.5 查看存储过程
desc 过程名;
desc user_source;
4. 函数 function
4.1 plsql中的函数和过程的区别
1) 关键字不同,过程是procedure 函数是function
2) 过程没有返回类型和返回值,函数有返回类型和返回值
3) 调用方式不同
过程在plsql中是直接调用
函数在plsql中需要组成表达式调用
(使用变量接收返回值、作为函数或过程的参数)
4.2 语法
create[ or replace] function 函数名[(参数列表)]
return 数据类型
is|as
-- 临时变量
begin
-- 必须有return语句
end;
4.3 创建一个函数,传入两个数字,返回最小值
create or replace function getmin_zsm_00(
var_a number,var_b number) return number
is
begin
if var_a < var_b then
return var_a;
else
return var_b;
end if;
end;
declare
var_x number:=100;
var_y number:=1;
var_result number;
begin
var_result:=getmin_zsm_00(var_x,var_y);
dbms_output.put_line(var_result);
dbms_output.put_line(getmin_zsm_00(123,10));
end;
5. 包 package
5.1 概念
把一组逻辑上相关的过程、函数、变量、类型等组织到一起的一种逻辑结构
5.2 系统提供的包
dbms_output: 输入输出包
dbms_random: 随机包
dbms_job: 定时任务调度包
查看包中的数据:
desc 包名;
desc dbms_random;
function value(low number,high number)
return number
包名.成员
select dbms_random.value(1,100) from dual;
5.3 自定义包
1)定义包 package -- 类似于C中.h文件
create [or replace] package 包名
is
-- 函数、过程的声明,类型的定义,变量的声明等
end[ 包名];
-- 定义一个包,包含一个过程和一个函数
create or replace package mypackage_zsm_00
is
procedure getmax(var_a number,var_b number);
function getmin(var_a number,var_b number)
return number;
end;
2) 定义包的主体(包的实现)
create [or replace] package body 包名
is
-- 函数、过程的实现
end[ 包名];
create or replace package body mypackage_zsm_00
is
procedure getmax(var_a number,var_b number)
is
begin
if var_a > var_b then
dbms_output.put_line(var_a);
else
dbms_output.put_line(var_b);
end if;
end;
function getmin(var_a number,var_b number)
return number
is
begin
if var_a < var_b then
return var_a;
else
return var_b;
end if;
end;
end;
-- 测试
declare
var_x number:=11;
var_y number:=1234;
var_res number;
begin
mypackage_zsm_00.getmax(var_x,var_y);
var_res:=mypackage_zsm_00.getmin(var_x,var_y);
dbms_output.put_line(var_res);
end;
6. 触发器 trigger
6.1 概念
触发器可以看成是一种特殊的"存储过程",它定义了一些和数据库相关事件(如insert、delete、update等)执行时应该执行的“功能代码块”,通常用来管理复杂的完整性约束、或监控对表的修改,甚至可以实现对数据的审计功能。
dml触发器
6.2 语法
create [or replace] trigger 触发器名
{before|after} {insert|update|delete}
on 表名 [for each row]
declare
begin
exception
end;
6.3 语句级触发器
emp_zsm_00
create or replace trigger tri_emp_zsm_00
before update on emp_zsm_00
declare
begin
dbms_output.put_line(‘table updated‘);
end;
update emp_zsm_00 set salary=salary + 100
where id=1;
update emp_zsm_00 set salary=salary + 100
where id<1;
update emp_zsm_00 set salary=salary + 100
where id>1;
6.4 行级触发器
create or replace trigger tri_emp_zsm_00
before update on emp_zsm_00 for each row
declare
begin
dbms_output.put_line(‘table updated‘);
end;
update emp_zsm_00 set salary=salary + 100
where id=1;
update emp_zsm_00 set salary=salary + 100
where id<1;
update emp_zsm_00 set salary=salary + 100
where id>1;
标识符:
:old 原值标识符
:new 新值标识符 表名%rowtype类型
insert :new
update :old :new
delete :old
create or replace trigger tri_emp_zsm_00
before update on emp_zsm_00 for each row
declare
begin
dbms_output.put_line(‘table updated‘);
dbms_output.put_line(‘old:‘||:old.id||‘,‘||:old.salary);
dbms_output.put_line(‘new:‘||:new.id||‘,‘||:new.salary);
end;
update emp_zsm_00 set salary=salary + 100
where id=1;
update emp_zsm_00 set salary=salary + 100
where id<1;
update emp_zsm_00 set salary=salary + 100
where id>1;
注意:触发器中不能包含任何有关事务的操作
事务 谁发起 谁结束
6.5 使用触发器产生主键的值
-- 创建一个表
create table testtrigger_zsm_00(
id number primary key,name varchar2(20));
-- 创建一个序列
create sequence trigger_id_zsm_00;
-- 创建触发器
create or replace trigger tri_pk_zsm_00
before insert on testtrigger_zsm_00 for each row
begin
select trigger_id_zsm_00.nextval into :new.id
from dual;
end;
-- 插入一条语句
insert into testtrigger_zsm_00(name) values(‘test1‘);
-----------------------------------------------------------------
练习:
1.使用游标 遍历s_dept表中的全部数据
2.创建一个存储过程,传入数字n(大于1的整数),计算1..n的累加和,保存在第二个参数,并测试。
--day07--
PROC
主要内容:
1) proc简介
2) proc程序的开发过程
3) 宿主变量和指示变量
4) 嵌入sql语句
5) 连接数据库
6) 错误处理
7) 数据的存取更新操作
8) 动态sql
-----------------------------
1.什么是pro程序?
1.1 概念
在过程化的编程语言中嵌入sql语句而开发出的应用程序,叫pro程序。
在通用的编程语言中嵌入的sql语句称为嵌入式sql
被嵌入了sql语句的编程语言称为宿主语言
1.2 proc/c++
在c/c++语言中嵌入sql语句而开发出的应用程序,称为proc/c++程序
目的:是C/C++这种高效的语言成为访问oracle数据库的工具
2. proc中嵌入的sql语句
#include .....
...
/* 包含一个名为sqlca的结构 */
exec sql include sqlca;
变量的声明
函数的声明
int main(void)
{
/* 连接数据库 */
exec sql connect:用户名/密码;
/* 操作数据库:比如查询 */
exec sql select 字段列表 into 变量列表 from 表名
where 条件;
/* 关闭并释放连接资源 */
exec sql commit work release;
exec sql rollback work release;
}
3.C程序的开发步骤
1) 编写源程序
vi xxx.c
2) 编译、链接
gcc xxx.c -lxxxxx
3) 运行
./a.out
4.proc程序的开发步骤
1) 编写源程序
vi xxxx.pc
2) 预编译
proc xxx.pc -----> xxx.c
3) 编译、链接
gcc xxx.c -lclntsh -- linux
gcc xxx.c -lorasql10(11) -- windows
4) 运行
./a.out
案例:first.pc
vi first.pc
proc first.pc
gcc first.c -lclntsh
./a.out
5. 宿主变量
5.1 概念
proc中C语言称为宿主语言
在宿主语言中定义,即可以在宿主语言中使用,也可以在sql语句中使用的变量,称为宿主变量。
5.2 宿主变量的数据类型
char 字符型
char var[n] 定长字符串
short
int 整型
long
float
double 浮点型
varchar var[n] 变长字符串
5.3 定长字符串和变长字符串
5.3.1 定长字符串
字符串的长度不足时用空格补齐
案例:charn.pc
5.3.2 变长字符串
案例:varcharn.pc
char name[25] ----> varchar name[25]
预编译时,varchar类型的数组被翻译成了同名的结构:
struct{
unsigned short len;
unsigned char arr[25];
} name;
varchar类型的数组在sql语句中使用时,和char型数组一样
在宿主语言中使用时,按照结构的用法:
提取字符串的值:name.arr
获取字符串的长度:name.len
使用varchar类型的数组时,可以会产生垃圾数据,解决方式:
1) 字符串进行初始化,把数组的所有元素初始化为‘\0‘
varchar name[25] = {0};
2) 手动为字符串添加结束标志
name.arr[name.len] = ‘\0‘;
5.3.3 使用预编译选项解决变长字符串的问题
oname 相当于 gcc 的 -o
char_map = charz: 处理成定长,空格补齐,‘\0‘结尾
= varchar2|charf: 处理成定长,空格补齐,不以‘\0‘结尾
= string: 处理成变长,以‘\0‘结尾
proc charn.pc char_map=string
gcc charn.c -lclntsh
./a.out
5.4 宿主变量使用时的注意事项
1) 在sql语句中使用宿主变量时,最好在变量名前加:
...
int id=2;
...
exec sql select first_name into name from s_emp
where id=id;
...
2) DDL语句中不允许使用宿主变量
案例:droptable.pc
.....
char tablename[20]="testdsql_zsm_00";
.....
/* 删除名为 tablename 的表 */
exec sql drop table tablename;
/* 预编译错误:宿主变量不能在ddl语句中使用 */
exec sql drop table :tablename;
3) 宿主变量可以使用指针,但是不推荐
4) 宿主变量的定义,强烈建议放入声明区
(C++语言、windows平台要求宿主变量的声明必须在声明区)
exec sql begin declare section;
/* 声明区 */
exec sql end declare section;
练习:定义合适的变量,接收s_dept中id=43的部门的信息,并输出。
案例:searchdeptbyid.pc
6.指示变量
6.1 指示变量的作用
当数据库中的字段的值,赋值给宿主变量时,赋值的状态了一通过指示变量获取。
指示变量的值:
0 代表正常赋值
-1 代表数据库中对应字段的值为null
>0 代表截断赋值 尽量避免(编译没问题,执行时在赋值阶段不会出现错误,引起的后续的问题 时机不确定)
6.2 语法
指示变量的数据类型必须是short
short indid;
short indname;
exec sql select id,first_name into :id,:name
from s_emp where id=1;
exec sql select 字段1,字段2 into
:宿主变量1 [indicator] :指示变量 , 宿主变量2
from 表名 where 条件;
exec sql select id,first_name into :id:indid,:name:indname
from s_emp where id=1;
6.3 案例:把s_emp表中id=1的员工的id,first_name,manager_id查询出来,保存到相应的宿主变量,同时使用指示变量指示赋值状态。
案例: indvar.pc
7.数组变量
7.1 数组变量使用的注意事项
1) 除了字符型外,其他类型的数组只能使用一维的
int ids[50];
char name[50][25];
2) 不支持数组指针
3) 最大元素个数 32767
4) 在select语句中使用数组变量时,只能给出数组的名字,不能使用下标
5) 如果要指示多个变量的赋值状态,可以使用指示变量数组
7.2 把s_emp表中的所有员工的id,first_name和manager_id查询出来,使用合适的数组接收查询结果,并使用指示变量数组指示manager_id的赋值状态
案例:arr_var.pc
8.sqlca通信区
8.1 通信区的概念
通信区:为了取得每个sql语句执行后的相关状态说明,以便进行错误的后续操作或跟踪
oracle中提供了两个通信区:
SQL通信区:sqlca
Oracle通信区:oraca
8.2 sqlca通信区
执行proc程序时,oracle把每一条sql中状态信息存入sqlca中,包括错误代码、警告标志设置、诊断文本和处理行数等。
本质上,sqlca是一个结构体
proc每执行一条sql语句,都会把相关信息写入到sqlca,覆盖上一条sql语句的执行的结果信息,所以需要获取执行结果信息的话,要执行完马上获取。
sqlca.sqlerrd[2]: sql语句执行后影响的行数
sqlca.sqlcode: sql执行的状态
0: 执行正常
>0: 执行出错(出现异常)
<0 : 数据库系统错误 或者网络错误
sqlca.sqlerrm.sqlerrmc: 错误信息
案例:sqlca.pc
9. oraca通信区
一个类似于sqlca的结构,可以作为sqlca通信区的补充。当需要获取更为详细的状态信息,可以使用oraca.
oraca通信区对sqlca的补充
可以使用oraca获取执行的sql语句的文本
oraca的使用步骤:
1) 包含oraca
exec sql include oraca;
2) 打开oraca option
exec oracle option(oraca=yes);
3) 设置sql文本的保存标志
oraca.orastxtf
0: 默认值 不保存
1: sql语句出错时保存
2: sql语句出现错误或警
告时保存
3: 都保存
4) 获取sql文本
oraca.orastxt.orastxtc
案例:oraca.pc
10. proc中如何使用sql语句
1) select语句
在语句前加exec sql,配合into使用
exec sql select 字段列表 into 宿主变量列表 from 表名
where 条件;
2) dml语句(insert delete update)
ddl语句(create drop alter)
tcl语句(commit rollback savepoint)
直接在语句前加exec sql
注意:ddl语句中不能使用宿主变量
综合案例:sql.pc
开发一个超小型的学生管理系统,完成以下功能:
创建学生信息表
添加学生信息
显示学生信息列表
根据学号更改信息
根据学号删除信息
循环菜单:
学生管理系统,请选择:
1.创建表
2.学生注册
3.查看
4.修改信息
5.删除信息
0.退出
练习:
--day08--
1. proc中如何使用plsql
1.1 使用plsql的语法
exec sql execute
begin
/* 相当于plsql的匿名块 */
end;
end-exec;
在预编译时,需要加如下两个选项:
sqlcheck = semantics
userid = 用户名/密码:在预编译时时,检查调用的过程、函数等子程序是否存在及合法
1.2 在proc中调用存储过程
1) 创建一个存储过程,传入两个参数,把两数之和存入第二个参数
create or replace procedure getsum_zsm_00(
var_a number,var_b in out number)
is
begin
var_b:=var_a + var_b;
end;
2) 在proc中调用存储过程
案例:callproc.pc
1.3 在proc中使用函数
1) 创建一个函数,传入两个数字,返回最大值
create or replace function getmax_zsm_00(
var_a number,var_b number) return number
is
begin
if var_a > var_b then
return var_a;
end if;
return var_b;
end;
2) 编写一个proc程序,调用该函数
案例:callfunc.pc
2. 连接数据库
2.1 本地数据库连接
exec sql connect:用户名/密码;
exec sql connect:用户名 identified by :密码;
2.2 远程连接数据库
需要提供一个远程连接的描述字符串,包括:ip地址、端口号、oracle数据库的服务ID(服务器名)
这个字符串有固定格式,程序员可以根据格式拼接字符串
a文件中,一般有远程连接的$ORACLE_HOME/network/admin/tnsnames.or描述字符串的配置
$ORACLE_HOME:环境变量,oracle的安装路径
ip地址是要访问的数据库所在的服务器的ip地址
端口号默认1521
有了远程数据库的描述字符串,使用using引入描述:
exec sql connect:用户名/密码 using :描述字符串;
案例:rdbconn.pc
如果面对的是多个数据库,可以他通过定义标签,然后使用标签区分访问的是哪个数据库。
案例:mdbconn.pc
3. proc中的错误处理
3.1 局部的错误处理方式
sqlca.sqlcode
sqlca.sqlerrm.sqlerrmc
3.2 全局的错误处理方式
错误处理方案只写一次,出现匹配的类型的错误,自动查找错误处理方案并执行
exec sql whenever 条件 动作;
条件就是错误的方式,有三种:
sqlerror: sql语句错误
notfound: 没有找到数据
sqlwarning: sql语句出现警告(不常用)
动作就是错误处理方案,包括:
do 错误处理函数(): 条件发生后,调用错误处理函数
do break: 退出循环
continue: 继续
stop: 停止执行代码(不常用)
goto 标签: 跳转到标签处(不常用)
3.3 举例:删除一张不存在的表
案例:sqlerror.pc
proc中默认对错误是忽略的(不提示、不中断)
sql语句出现错误,会向上查找对应的全局处理语句,如果有,执行处理方案。如果没有,继续执行后面的代码。
4. proc中的数据处理
1) 单行单列的结果集 使用一个宿主变量解决
....
char name[25];
.....
exec sql select first_name into :name from s_emp
where id=1;
.....
2) 单行多列的结果集 使用多个宿主变量 或 结构体变量 解决
exec sql select id,first_name,salary into :id,:name,:sal
from s_emp where id=1;
案例:selecta.pc
3) 多行单列的结果集 使用一个数组解决
exec sql select first_name into :names from s_emp;
4) 多行多列的结果集 使用多个数组、结构体数组、游标解决
exec sql select id,name,region_id into :ids,:names,:rids
from s_dept;
练习:使用多个数组接收s_dept表中的全部信息,并循环输出。
添加必要的错误处理
案例:selectb.pc
在proc中使用游标的步骤:
a.声明游标
exec sql declare 游标名 cursor for select语句;
b. 打开游标
exec sql open 游标名;
c. 提取数据
exec sql fetch 游标名 into :宿主变量;
d.关闭游标
exec sql close 游标名;
使用游标时,结束循环采用:
exec sql whenever notfound do break;
案例:selectc.pc
有一种特殊的游标,叫做滚动游标。滚动游标可以直接调到任何一行。在用法上和普通游标(顺序游标)的区别:
a. 声明时,滚动游标关键字是scroll cursor
exec sql declare 游标名 scroll cursor for select语句;
b. 提取数据时,滚动游标有多种方式:
fetch first 提取第一行
fetch last 提取最后一行
fetch next 提取下一行
fetch prior 提取上一行
fetch absolute n: 提取第n行
fetch relative n 提取从当前位置开始的第n行
fetch current 提取当前行
exec sql fetch first 游标名 into :宿主变量
案例:cp selectc.pc scrollcursor.pc
5.动态sql
sql在执行时,才由参数传入
1) 非select语句,没有占位符
char sqlstr[100]="create table....";
exec sql execue immediate :sqlstr;
案例:dsqla.pc
2) 非select语句,有占位符
char sqlstr[100]="insert into.....values(:b0,:b1...)";
exec sql prepare stmt from :sqlstr;
exec sql execute stmt using :宿主变量....;
案例:dsqlb.pc
3) select语句
char sqlstr[100]="select .... from ... where id>:b0";
exec sql prepare stmt from :sqlstr;
exec sql declare 游标名 cursor for stmt;
exec sql open stmt using :ids ,:names;
....
案例:dsqlc.pc
----------------------------------------------------------------------
总结:
1. sql
select
select..from
表达式
别名
null值: nvl
where子句
比较运算符
between and
in
like _ % escape ‘\‘
is null
逻辑运算符:and or not
order by 子句
order by 排序标准 排序方式,排序标准 排序方式
group by子句
select后的字段必须是分组标准,或者是组函数
having子句
分组后的筛选
单行函数
to_date to_char
组函数
表连接
select 字段列表
from 表1,表2 where 关联条件 [and 筛选条件];
(+):oracle中外连接
select 字段列表
from 表1 {{left|right|full} [outer] | [inner]} join 表2
on 关联条件 [where 筛选条件];
子查询
where
select
having
from
ddl:
table
sequence
view
index
dml
insert delete update
tcl
ACID:
约束
分页
2. plsql
变量
数据类型: record、table、 ref cursor
表名.字段%type
表名%rowtype
流程控制
动态sql
游标
异常
有名块
过程
函数
包
触发器
3.proc
宿主变量
指示变量
数组变量
通信区
嵌入sql、plsql语句
连接数据库
错误处理
select结果集的处理
动态sql
---------------------------------------------------------------------