视图
一、什么是视图
- 能看到的,可视化,是一个虚表
- 视图是一张虚表,封装了一个复杂的sql查询语句
- 可以对视图进行增删改查,视图不能存储记录,所有的数据都在基本表中(table)
- 操作视图就是操作基本表
- 视图一般只能用来做查询
二、视图的语法:如果权限不足,可以切换system用户来提高当前用户的权限
create [or replace] view 视图名 as sql查询语句;
sql语句的语法中:[]
代表可有可无
三、举例
select * from emp_view;
select * from emp;
insert into emp_view(empno,ename) values(2001,'LiSi')
数据增加之后,在原来的表中也添加了数据。
所以,操作视图,就是操作基本表
四、作用
- 屏蔽敏感数据
create or replace view employee as select empno,ename,job,mgr,hiredate,deptno from emp;
注意:CRUD操作只能针对于你视图中存在的列
- 创建一个只读的视图
create or replace view employee as select empno,ename,job,mgr,hiredate,deptno from emp with read only;
- 封装了一个复杂的sql语句
索引
一、什么是索引
目录就是索引
索引创建的三大前提
- 百万条记录以上
- 经常查询使用的列
- 不经常修改的列
原因:修改列会导致索引重建
注意:主键列和唯一列都是索引列,不能添加索引
二、索引的作用
提高查询的效率
三、索引的语法
对这张表的哪些列来创建索引
create index 索引的名称 on 表(列,...);
四、测试单列索引的效率
- 创建表
create table a(
id number primary key,
text varchar2(200)
)
- 添加百万条数据
-- 创建序列
create sequence aseq;
-- 获取uuid
--select sys_guid() from dual;
declare
begin
for i in 1..1000000 loop
insert into a values(aseq.nextval,sys_guid());
end loop;
end;
- 添加索引前查询,约0.3s
-- 添加索引前查询
select * from a where text = 'A7CCD10B185546BBB52D66D45622B431'
- 添加索引
--创建索引
create index a_index on a(text)
- 添加索引后查询,约0.03s
select * from a where text = 'A7CCD10B185546BBB52D66D45622B431'
五、复合索引
如果,给两列以上来创建索引:
表名(name,address)
可以触发索引
select * from 表 where name=' ' and address = ''
不能触发索引:or会触发全表扫描
select * from 表 where name=' ' or address = ''
可以触发索引
select * from 表 where name=' '
不能触发索引:创建索引时后边的列名
select * from 表 where address = ''
plsql的基本语句(过程化语言)
基本语句
在sql语句的基础上,新增了if、循环等操作
plsql的基本结构
declare
--声明
声明变量的内容begin
--开始
过程化语言,sql[exception]
--异常
捕获异常end
--结束
--声明变量
declare
-- 声明变量
-- 声明普通的变量
i number;
-- 声明变量且赋值
j number default 200;
-- 声明变量,可以指定为引用类型
-- pname的类型就是emp表中ename列的类型
pname emp.ename%type;
pjob emp.job%type;
-- 声明记录类型变量:可以记录一行的类型
e_row emp%rowtype;
begin
-- 赋初始值
i:=100;
-- 打印语句
dbms_output.put_line(i);
-- ||oracle中的连接符
-- ''表示一个字符串,""创建别名时可以使用
dbms_output.put_line('j='||j);
-- 将查询出来的姓名打印到控制台上
-- select .. into .. :select 查询某些列into赋值给某些变量
select ename into pname from emp where empno=7788;
dbms_output.put_line(pname);
-- 查询两列
select ename,job into pname,pjob from emp where empno=7788;
dbms_output.put_line(pname);
dbms_output.put_line(pjob);
-- 查询该表的所有列
select * into e_row from emp where empno=7788;
dbms_output.put_line(e_row.empno||e_row.ename||e_row.job);
end;
结果
100
j=200
SCOTT
SCOTT
ANALYST
7788SCOTTANALYST
if语句
语法一:
if 条件 then
sql语句;
end if;
语法二:
if 条件 then
sql语句;
else
sql语句;
end if;
语法三:
if 条件 then
sql语句;
elsif 条件 then
sql语句;
elsif 条件 then
sql语句;
...
else
sql语句;
end if;
declare
i number;
begin
i:=&请输入;
if i >0 then
dbms_output.put_line('正数');
elsif i<0 then
dbms_output.put_line('负数');
else
dbms_output.put_line('0');
end if;
end;
循环语句
语法一:
for i in 集合|游标 loop
过程化语言
end loop;
在控制台打印1-10所有的整数
declare
begin
for i in 1..10 loop
dbms_output.put_line(i);
end loop;
end;
语法二:
loop
循环体;
结束条件: exit when 条件;
end loop
在控制台打印1-10所有的整数
declare
i number default 1;
begin
loop
dbms_output.put_line(i);
exit when i = 10;
i:=i+1;
end loop;
end;
语法三
while 条件 loop
循环体;
end loop
在控制台打印1-10所有的整数
declare
i number default 1;
begin
while i<=10 loop
dbms_output.put_line(i);
i:=i+1;
end loop;
end;
游标
本质上是一个集合,存储大量的数据
游标主要解决返回多行多列的问题
select into
只能处理一行数据的问题,要处理多行数据的问题:使用游标
声明游标
cursor 游标名 is sql查询语句;
遍历游标
- 打开游标
open 游标名
- 提取一行的数据
fetch 游标名 into 变量;
-- 把游标中一行的内容读入到变量中
- 循环,遍历所有数据,退出
exit when 游标名%notfound;
-- 没有提取到记录,就退出
- 关闭游标
close 游标名
在控制台上打印20号部门的所有员工信息
declare
-- 声明游标
cursor cur is
select * from emp where deptno = 20;
e_row emp%rowtype;
begin
-- 遍历游标
open cur;
loop -- 开始循环
fetch cur into e_row;
exit when cur%notfound; --没有就退出
dbms_output.put_line('编号:'||e_row.empno||'姓名'||e_row.ename);
end loop; --结束循环
close cur;
end;
对20号部门所有的员工涨工资
declare
cursor cur is
select empno from emp where deptno = 20;
begin
-- 涨工资
-- 注意,for循环会自动打开和关闭游标
-- 注意:i表示的是一行
for i in cur loop
-- dbms_output.put_line(i.empno);
update emp set sal = sal + 1 where empno = i.empno;
end loop;
end;
一般会结合存储过程使用
存储过程
存储过程:把过程存储起来,里面封装了一组sql语句,且提前编译好,存储在服务端。
优点:安全,效率高
一般由项目经理和dba编写存储过程
使用场景:买了一件商品,数据库的变化过程就可以存储到存储过程中
存储过程的语法
参数分类:
- 输入型参数
- 输出型参数
create or replace procedure 过程名(参数列表:参数1 in|out 数据类型,参数2 in|out 数据类型...)
is|as --相当于plsql基本结构中的declare
begin
end;
示例一:给某员工涨工资
-- 需要的参数
-- 员工编号(输入型,number),涨多少(输入型,number)
create or replace procedure updateSal(eno in number,psal in number)
is --相当于plsql基本结构中的declare
oldSal emp.sal%type;
newSal emp.sal%type;
begin
-- 查询工资:涨前
select sal into oldSal from emp where empno = eno
dbms_output.put_line('涨前'||oldSal);
update emp set sal = sal + psal where empno = eno;
-- 查询工资:涨后
select sal into newSal from emp where empno = eno;
dbms_output.put_line('涨后'||newSal);
end;
-- 访问存储过程:只有输入型参数
call updateSal(7788,200);
示例二:获取某员工的年薪
存储过程
-- 获取某员工的年薪
-- 需要的参数:员工编号(输入项,number)
-- 返回:年薪(输出型,number)
create or replace procedure getYearSal(eno in number,yearSal out number)
is --相当于plsql基本结构中的declare
begin
select sal*12 + nvl(comm,0) into yearSal from emp where empno = eno;
end;
访问存储过程:有输入有输出
--访问存储过程:访问有输入有输出的存储过程
declare
--输出型的变量,需要声明一个变量来接收参数的值
yearSal number;
begin
getYearSal(7788,yearSal);
-- 打印年薪
dbms_output.put_line(yearSal);
end;
示例三:查询某部门所有的员工信息
输出参数为游标类型,游标类型
- 静态的游标类型(
cursor
):在声明游标时指定sql语句 - 动态的游标类型(
sys_refcursor
):在使用时指定sql语句
create or replace procedure getEmps(dno in number,emps out sys_refcursor)
as
begin
-- 给动态游标指定sql语句
open emps for select * from emp where deptno = dno;
-- 查询了某部门的员工信息 select * from emp where deptno = dno;
end;
注意不能使用for循环
-- 访问存储过程:有输入有输出
declare
emps sys_refcursor;
--声明变量,接收游标的行内容
e_row emp%rowtype;
begin
getEmps(30,emps);
-- 遍历游标
loop
fetch emps into e_row;
-- 退出
exit when emps%notfound;
dbms_output.put_line(e_row.empno||e_row.ename);
end loop;
end;
存储函数
和存储过程的定义相同
注意:
- 存储过程和存储函数属于同一类对象,名称不能重复,且不可替换
- 存储函数一般没有输出参数
存储函数和存储过程的区别
- 存储函数必须有一个返回值
- 可以在select语句中访问
存储函数的语法
create or replace function 函数名(参数列表:参数1 in|out 数据类型,参数2 in|out 数据类型,...)
return 数据类型
is|as
begin
return 值;
end;
获取某员工的年薪
-- 参数:员工编号 in number,年薪 out number(返回值)
create or replace function getYearSalFun(eno in number) return number
is
yearsal number;
begin
select sal*12 +nvl(comm,0) into yearsal from emp where empno=eno;
return yearsal;
end;
-- 访问存储函数
declare
yearsal number;
begin
yearsal:= getYearSalFun(7788);
dbms_output.put_line(yearsal);
end;
触发器
触发器:简单来说,就是监听器
作用:监听表中的数据是否发生了改变(增删改)
语法:
create or replace trigger --触发器名称
before|after --在改变数据之前(或之后)执行触发器
insert|update|delete --监听的动作
on 表 --监听哪张表
触发器的级别
declare
begin
end
添加一条记录,在控制台打印:打印一条语句
-- 添加一条记录,在控制台打印:打印一条语句
create or replace trigger insertPrint
after
insert
on emp
declare
begin
dbms_output.put_line('添加了一条记录');
end;
insert into emp(empno,ename) values(2002,'wangwu');
不能给员工降薪
create or replace trigger notUpdateLowerSal
before
update
on emp
--触发器级别
--表级触发器:默认
-- 更新多条记录执行一次
--行级触发器:for each row
-- 更新一条记录执行一次
--注意:使用了old,new关键字,必须使用行级触发器
for each row
declare
begin
-- 判断涨前大于涨后
-- 相反,不管
-- 得到之前的工资:old.sal
-- 得到之后的工资:new.sal
if:old.sal>:new.sal then
dbms_output.put_line('不能降薪');
-- 弹出提示窗口
-- raise_application_error(p1,p2);
--p1:错误编号 -20001到-20999
--p2:错误信息
raise_application_error(-20001,'不能降薪!!');
end if;
end;
update emp set sal=sal-1 where empno=7788
jdbc
ojdbc测试
一、引入依赖:可以搜missing ojdbc来解决jar包问题
<!--该jar包是oracle的驱动jar包-->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc14</artifactId>
<version>10.2.0.2.0</version>
</dependency>
二、测试
public class TestJDBC {
String driver = "oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@192.168.137.128:1521:orcl";
String username = "scott";
String password = "tiger";
Connection conn;
PreparedStatement pst;
ResultSet rs;
@Before
public void init(){
//注册驱动
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//获取连接
try {
conn = DriverManager.getConnection(url,username,password);
} catch (SQLException e) {
e.printStackTrace();
}
}
/*
查询员工表
*/
@Test
public void test() throws SQLException {
//sql语句
String sql = "select * from emp";
//创建预编译对象
pst = conn.prepareStatement(sql);
//设置参数
//执行sql语句
rs = pst.executeQuery();
//处理结果集
while (rs.next()){
System.out.println(rs.getInt("empno")+rs.getString("ename"));
}
}
@After
public void close(){
//关闭资源
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//关闭资源
if(pst!=null){
try {
pst.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//关闭资源
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
jdbc执行SQL存储过程和存储函数
一、执行存储过程的sql语句写法
{call <procedure-name>[(<arg1>,<arg2>, ...)]}
使用get(输出参数的索引)
函数来获取结果
第一种:输出类型为int类型
@Test
public void test2() throws SQLException {
//sql语句
String sql = "{call getYearSal(?,?)} ";
//创建预编译对象
cst = conn.prepareCall(sql);
//设置参数
//输入型参数
cst.setInt(1,7788);
//输出型参数:zh
cst.registerOutParameter(2, OracleTypes.NUMBER);
//执行sql语句
cst.execute();
//处理结果集
int ys = cst.getInt(2);
System.out.println(ys);
}
第二种:输出类型为游标类型
使用getObject
接收游标,并转换为结果集
@Test
public void test3() throws SQLException {
//sql语句
String sql = "{call getEmps(?,?)} ";
//创建预编译对象
cst = conn.prepareCall(sql);
//设置参数
//输入型参数
cst.setInt(1,20);
//输出型参数:zh
cst.registerOutParameter(2, OracleTypes.CURSOR);
//执行sql语句
cst.execute();
//处理结果集
Object object = cst.getObject(2);
rs = (ResultSet) object;
while (rs.next()){
System.out.println(rs.getInt("empno")+rs.getString("ename"));
}
}
二、执行存储函数的sql语句写法
{?= call <procedure-name>[(<arg1>,<arg2>, ...)]}
和存储过程没什么区别
@Test
public void test4() throws SQLException {
//sql语句
String sql = "{?= call getYearSalFun(?)}";
//创建预编译对象
cst = conn.prepareCall(sql);
//设置参数
//输入型参数
cst.setInt(2,7788);
//输出型参数:zh
cst.registerOutParameter(1, OracleTypes.NUMBER);
//执行sql语句
cst.execute();
//处理结果集
int ys = cst.getInt(1);
System.out.println(ys);
}