作用于一行,返回一个值
select upper("yse") from dual;--结果:YES
select lower("YES") from dual;--结果:yse
select round(26.18,1) from dual; --四舍五入,后面参数表示保留的小数, 整数==往后保留,负数==往前保留
select trunc(56.16,1) from dual; --直接截取,不再看后面位数是否大于5
select mod(10,3) from dual; --求模(取余) 结果:1
--查询出emp表中所有员工入职距离现在几天
select sysdate-e.hiredate
from emp e;--sysdate==当前系统时间
--算出明天此刻
select sysdate+1
from dual;
--查询出emp表中所有员工入职距离现在几月
select months-between(sysdate,e.hiredate)
from emp e;
--查询出emp表中所有员工入职距离现在几年
select months-between(sysdate,e.hiredate)/12
from emp e;
--查询出emp表中所有员工入职距离现在几周
select round((sysdate-e.hiredate)/7)
from emp e;round()==四舍五入函数
--时间转字符串格式
select to_char(sysdate,'fm yyyy-mm-dd hh24:mi:ss')
from dual; fm==时分秒前不加0 24==24小时制
--字符串转日期
select to_char('2020-7-13 22:41:21','fm yyyy-mm-dd hh24:mi:ss')
from dual;
-- 算出emp表中所有员工的年薪
--sal=月薪字段 e=emp的别名 comm=一年的奖金
select e.sal*12+e.comm
from emp e;
--奖金里面有null时,null值和任意数字做算数运算,结果都是null
--解决:nvl()函数:如果值为null用0代替
select e.sal*12+nvl(e.comm,0)
from emp e;
作用于多行,返回一个值
方法 | |
---|---|
count(1) | 查询总数量, 1= * 表示主键即所有 |
sum(‘字段’) | 求和 |
max(‘字段’) | 最大值 |
min(‘字段’) | 最小值 |
avg(‘字段’) | 平均值 |
-- 查询出每个部门的平均工资
select e.deptno, avg(e.sal) --如果加这段:,e.ename 则报错
from emp e
group by e.deptno
--查询出平均工资高于2000的部门信息
select e.deptno, avg(e.sal) asal --如果加这段及haveing后面用asal来判断则报错
from emp e
group by e.deptno
having avg(e.sal)>2000;
如:select ename,sal s from emp where s>1500; --会报错,s别名不能用来判断
where和having的区别
子查询返回一个值
-- 查询工资和员工SCOTT一样的员工信息
select *
from emp
where sal = (-- 由于ename字段不是主键,姓名为'SCOTT'的数据可能不止一条,所以上面用等号有时会有问题 解决:用in代替等号即可
select sal
from emp
where ename = 'SCOTT';
)
子查询返回一个集合
-- 查询出工资和10号部门任意员工一样的员工信息
select *
from emp
where sal in (
select sal
from emp
where deptno = 10;
) -- deptno==部门编号
子查询返回一张表
-- 查询出每个部门最低工资,和最低工资员工姓名,和该员工所在部门名称
-- 三表联查,得到最终结果
select t.deptno ,t.msal,e.ename,d.dname
from(
select deptno min(sal) msal --1-先查询每个部门最低工资 msal==最小工资字段别名
from emp
group by deptno
) t,emp e,dept d
where t.deptno = e.deptno and t.msal = e.sal and e.deptno = d.deptno;
rownum行号:当我们做select操作的时候,每查询出一行记录,就会在该行上加一个行号,行号从1开始,依次递增,不能跳着走
-- 例:emp表工资倒叙排序后,每页五条记录,查询第二页
-- 第二页即:行号为6-10
-- 说明:排序操作会影响rownum的顺序 如:
select *
from emp e
where rownum < 4
order by e.sal desc; --查询排序后的前三条
解决:
select rownum,t.*
from(
select *
from emp e
order by e.sal desc
) t
where rownum<4; -- 先排序再加行号
-- 说明:rownum行号不能使用大于>
select rownum,e.*
from(select *
from emp e
order by e.sal desc
)
where rownum<11 and rownum>5 -- 查询无数据
-- 正确分页查询语句:
select *
from(
select rownum rn,tt.*
from(
select *
from emp
order by sal desc
) tt
order by e.sal desc
where rownum<11
)
where rn>5;
概念:视图就是提供一个查询的窗口,所有数据来自于原表
视图的作用:
--查询语句创建表
create table emp
as
select *
from scott.emp; --scott==用户名
-- 查询
select *
from emp;
--创建视图[必须有dba权限]
create view v_emp
as
select ename,job
from emp;
--查询视图
select * from v_emp;
--修改视图(不推荐)
update v_emp
set job='CLERK'
where ename='ALLEN';--原表数据也被修改
commit;
--创建只读视图
create view v_emp1
as
select ename,job
from emp with read only;--无法修改
概念:索引就是在表的列上构建一个二叉树。达到大幅度提高查询效率的目的,但是索引会影响增删改的效率
单列索引
-- 创建单列索引
create index idx_ename on emp(ename);
-- 触发规则:条件必须是索引列中的原始值。
-- 单行函数,模糊查询 都会影响索引的触发
select * from emp where ename='SCOTT';
复合索引
--创建复合索引
create index idx_enamejob on emp(ename,job);
--触发规则:复合索引中第一列为优先检索列。如果要触发复合索引,必须包含有优先检索列中的原始值
select * from emp where ename='SCOTT' and job='xxx'; --会触发复合索引,有优先检索列ename='SCOTT'
select * from emp where ename='SCOTT' or job='xxx'; --不触发索引。相当于两个语句,一个触发一个不触发即不触发。
select * from emp where ename='SCOTT'; --触发的是单列索引,既有单列也有复合,但查询为单列
声明方法:
declare
--定义一个数字变量i赋值为10
i number(2) := 10;
--定义字符串变量s赋值为小明
s varchar2(10) := '小明';
--引用型变量,定义一个emp表中ename字段类型的变量ena
ena emp.ename%type;
--记录型变量,定义一个存emp表中一行记录的变量,相当于一个对象
emprow emp%rowtype;
begin
dbms_output.put_line(i); --输出变量i 结果:10
dbms_output.put_line(s); --输出变量s 结果:小明
select ename into ena
from emp
where empno=7788; --从emp表中查询一个姓名赋值给变量ena
dbms_output.put_line(ena); --输出变量ena
select * into emprow
from emp
where empno=7788;
dbms_output.put_line(emprow.ename ||'的工作为:'|| emprow.job); --结果例如==SCOTT的工作为:ANALYST
end;
--总结:
赋值操作可以用【:=】也可以用【into查询语句】赋值
if判断:
--输入小于18的数字,输出未成年;输入大于18小于40数字,输出中年人;输入大于40的数字,输出老年人
declare
i number(3) := ⅈ --输入一个数值类型的数据并赋值给i
begin
if i<18 then
dbms_output.put_line('未成年');
elsif i<40 then
dbms_output.put_line('中年人');
else
dbms_output.put_line('老年人');
end if;
end;
loop循环:
--用三种方式输出1-10
-------------------------------------------while循环
declare
i number(2) :=1;
begin
while i<11 loop
dbms_output.put_line(i);
i := i+1;
end loop;
end;
-------------------------------------------exit循环
declare
i number(2) :=1;
begin
loop
exit when i>10;
dbms_output.put_line(i);
i := i+1;
end loop;
end;
-------------------------------------------for循环
declare
begin
for i in 1..10 loop
dbms_output.put_line(i);
end loop;
end;
概念:可以存放多个对象,多行记录,相对于集合
-- 输出emp表中所有员工的姓名及工作
declare
cursor c1 is
select *
from emp; --定义了一个游标c1,并把emp表中所有数据存入了游标
emprow emp%rowtype; --记录型变量,定义一个存emp表中一行记录的变量,相当于一个对象
begin
open c1;--打开游标
loop
fetch c1 into emprow; --把emp一行数据复制给变量emprow
exit when c1%notfound; --当游标找不到数据即最后一行时退出循环
dbms_output.put_line(emprow.ename ||'--'|| emprow.job);
end loop;
close c1;--关闭游标
end;
--给指定部门员工涨工资
declare
cursor c2(eno emp.deptno%type) is
select empno
from emp
where deptno=eno; --定义了一个游标并需要传入一个参数eno
en emp.empno%type;
begin
open c2(10);--打开游标并传入参数10
loop
fetch c2 into en;
exit when c2%notfound;
update emp set sal = sal+100 where empno = en;
commit;
end loop;
cloce c2;
end;
概念:存储过程就是提取已经编译好的一段pl/sql语言,放置在数据库端,可以直接被调用,这一段pl/sql一般都是固定步骤的业务。
--创建存储过程语法:
create [or replace] procedure 过程名[参数名 in/out 数据类型...]
AS 或 is
begin
plsql子程序体;
end;
-- 例:给指定员工涨100块钱
create or replace procedure pl(eno emp.empno%type) --or replace表示如果已经存在该存储过程仍可以正常执行
is
begin
update emp set sal = sal+100 where empno = eno;
commit;
end;
--测试存储过程p1
declare
begin
pl(7788); --结果:员工编号为7788的员工工资涨100
end;
out类型参数如何使用
--使用存储过程来算年薪
create or replace procedure p_yearsal(eno emp.empno%type,yearsal out number) --yearsal out number表示yearsal参数为输出参数
is
s number(10); --存放工资
c emp.comm%type; --存放奖金
begin
select sal*12,nvl(comm,0) into s,c
from emp
where empno=eno;
yearsal := s+c;
end;
--测试存储过程p_yearsal
declare
yearsal number(10); --定义变量放入存储过程改变其值
begin
p_yearsal(7788,yearsal); --yearsal的值在存储过程内被改变
dbms_output.put_line(yearsal); --结果:输出员工编号为7788的员工的年薪
en
in和out类型参数的区别:凡是涉及到into查询语句赋值或 := 赋值操作的参数,都必须使用out来修饰 否则其余都用in
与存储过程区别:
--通过存储函数实现计算指定员工的年薪
create or replace function f_yearsal(eno emp.empno%type) return number --这里参数和返回值类型不能带长度
is
s number(10);
begin
select sal*12+nvl(comm,0) into s
from emp
where empno = eno;
return s;
end;
--测试存储函数f_yearsal
--存储函数在调用的时候,返回值需要接收,不接受会报错
declare
sal_year number(10);
begin
sal_year := f_yearsal(7788);
dbms_output.put_line(sal_year);--结果:输出员工编号为7788的员工的年薪
end;
存储过程和存储函数的区别:
制定一个规则,在【增删改】操作的时候,只要满足该规则,自动触发,无需调用。
-- 例:插入一条记录,输出'创建xxx'
--创建一个触发器t1
create or replace trigger t1
after --语句执行前还是后执行该触发器
insert --执行什么语句会触发
on person --作用哪个表的触发器
declare
begin
dbms_output.put_line('创建了一个person');
end;
--触发t1
insert into person(pid, name, gender, birthday) values(2, '李四', 1, to_date('2020-7-17', 'yyyy-MM-dd'));
行级触发器:
包含有for each row的就是行级触发器。
加for each row是为了使用 :old 或者 :new 对象获取一行记录
-- 例:不能给员工降薪
create or replace trigger t2
before
updater
on emp
for each row --表示行级触发器
declare
begin
if :old.sal > :new.sal then
-- 抛出一个异常raise_application_error(-20001~-20999之间,'错误提示信息');
raise_application_error(-20001,'不能给员工降薪');
end if;
end;
-- 例:使用触发器实现主键自增
--分析:在用户插入操作之前,拿到即将插入的数据,给该数据中的主键列赋值,使用该功能用到行级触发器
create or replace trigger auid
before
insert
on person
for each row
declare
begin
select s_id_person.nextval into :new.pid from dual;
--s_person 自定义序列号名字
--nextval 取序列的下一个值
--dual 是oracle的bai虚拟表du,不是真实存在的
end;
--需要jar包
oracle10g == ojdbc14.jar
oracle11g == ojdbc6.jar
--需要接口
CallableStatement
public class OracleDemo {
//测试连接oracl数据库
@Test
public void javaCallOracle() throws Exception {
//加载数据库驱动
Class.forName("oracle.jdbc.driver.OracleDriver");
//得到Connection连接
Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@192.168.88.6:1521:orcl",
"root", "root");
//得到预编译的Statement对象
PreparedStatement pstm = connection.prepareStatement("select * from emp where empno = ?");
//给参数赋值
pstm.setObject(1, 7788);
//执行数据库查询操作
ResultSet rs = pstm.executeQuery();
//输出结果
while(rs.next()){
System.out.println(rs.getString("ename"));
}
//释放资源
rs.close();
pstm.close();
connection.close();
}
/** * java调用存储过程 * {?= call <procedure-name>[(<arg1>,<arg2>, ...)]} 调用存储函数使用 * {call <procedure-name>[(<arg1>,<arg2>, ...)]} 调用存储过程使用 * @throws Exception */
@Test
public void javaCallProcedure() throws Exception {
//加载数据库驱动
Class.forName("oracle.jdbc.driver.OracleDriver");
//得到Connection连接
Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@192.168.88.6:1521:orcl",
"root", "root");
//得到预编译的Statement对象
CallableStatement pstm = connection.prepareCall("{call p_yearsal(?, ?)}");//call p_yearsal(?, ?)为存储过程,带两个参数
//给参数赋值
pstm.setObject(1, 7788);//第一个参数,查询的id
pstm.registerOutParameter(2, OracleTypes.NUMBER);//无返回值,第二参数表示输出类型的参数==员工年薪
//执行数据库查询操作
pstm.execute();
//输出结果[第二个参数]
System.out.println(pstm.getObject(2));
//释放资源
pstm.close();
connection.close();
}
/** * java调用存储函数 * {?= call <procedure-name>[(<arg1>,<arg2>, ...)]} 调用存储函数使用 * {call <procedure-name>[(<arg1>,<arg2>, ...)]} 调用存储过程使用 * @throws Exception */
@Test
public void javaCallFunction() throws Exception {
//加载数据库驱动
Class.forName("oracle.jdbc.driver.OracleDriver");
//得到Connection连接
Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@192.168.88.6:1521:orcl",
"root", "root");
//得到预编译的Statement对象
CallableStatement pstm = connection.prepareCall("{?= call f_yearsal(?)}");
//给参数赋值
pstm.setObject(2, 7788); //第二个?表示存储函数参数==查询的id
pstm.registerOutParameter(1, OracleTypes.NUMBER);//第一?表示存储函数返回值
//执行数据库查询操作
pstm.execute();
//输出结果[第一个参数]
System.out.println(pstm.getObject(1));
//释放资源
pstm.close();
connection.close();
}
}