Oracle Database,又名Oracle RDBMS,或简称Oracle。是甲骨文公司的一款关系数据库管理系统。它是在数据库领域一直处于领先地位的产品。可以说Oracle数据库系统是目前世界上流行的关系数据库管理系统,系统可移植性好、使用方便、功能强,适用于各类大、中、小、微机环境。它是一种高效率、可靠性好的适应高吞吐量的数据库解决方案。
平常所说的Oracle可以指Oracle数据库管理系统。Oracle数据库管理系统是管理数据库访问的计算机软件(Oracle database manager system)。它由Oracle 数据库和Oracle 实例( instance)构成(区分mysql, mysql没有实例的概念)。
数据库(database): 物理操作系统文件或磁盘的集合。
Oracle实例 : 位于物理内存的数据结构,它由操作系统的多个后台进程和一个共享的内存池所组成,共享的内存可以被所有进程访问。Oracle用它们来管理数据库访问。用户如果要存取数据库(也就是硬盘上的文件〉里的数据,必须通过Oracle实例才能实现,不能直接读取硬盘上的文件。实际上,Oracle 实例就是平常所说的数据库服务( service)。在任何时刻,一个实例只能与一个数据库关联,访问一个数据库; 而同一个数据库可由多个实例访问(RAC)
Oracle数据库是数据的物理存储。这就包括(数据文件ORA或者DBF、控制文件、联机日志、参数文件)。其实Oracle数据库的概念和其它数据库不一样,这里的数据库是一个操作系统只有一个库。可以看作是Oracle就只有一个大数据库。
一个Oracle实例(Oracle Instance)有一系列的后台进程(Backguouud Processes)和内存结构(Memory Structures)组成。一个数据库可以有n个实例。
注: 一组Oracle后台进程/线程以及一个共享内存区,这些内存由同一个计算机上运行的线程/进程所共享。这里可以维护易失的、非持久性内容(有些可以刷新输出到磁盘)。就算没有磁盘存储,数据库实例也能存在。
用户是在实例下建立的。不同实例可以建相同名字的用户。用户登陆后只能看到和操作自己的表, ORACLE 的用户与 MYSQL 的数据库类似,每建立一个应用需要创建一个用户。
注: Oracle 数据库建好后,要想在数据库里建表,必须先为数据库建立用户,并为用户指定表空间
表空间是Oracle对物理数据库上相关数据文件(ORA或者DBF 文件)的逻辑映射。一个数据库在逻辑上被划分成一到若干个表空间,每个表空间包含了在逻辑上相关联的一组结构。每个数据库至少有一个表空间(称之为system表空间)。
注: 每个表空间由同一磁盘上的一个或多个文件组成,这些文件叫数据文件(datafile)。一个数据文件只能属于一个表空间。
数据文件 是 数据库的物理存储单位。数据库的数据是存储在表空间中的,真正是在某一个或者多个数据文件中。而一个表空间可以由一个或多个数据文件组成,一个数据文件只能属于一个表空间。一旦数据文件被加入到某个表空间后,就不能删除这个文件,如果要删除某个数据文件,只能删除其所属于的表空间才行
注: 表的数据,是由用户放入某一个表空间的,而这个表空间会随机把这些表数据放到一个或者多个数据文件中。由于oracle的数据库不是普通的概念,oracle是由用户和表空间对数据进行管理和存放的。但是表不是由表空间去查询的,而是由用户去查的。因为不同用户可以在同一个表空间建立同一个名字的表! 这里区分就是用户了!
表空间? ORACLE数据库的逻辑单元。数据库–表空间 一个表空间可以与多个数据文件(物理结构)关联
一个数据库下可以建立多个表空间,一个表空间可以建立多个用户、一个用户下可以建立多个表。
create tablespace jiangqi
datafile 'F:\OracleDataBase\text.dbf'
size 100m
autoextend on
next 10m;
注意:
jiangqi:表空间名称
datafile :指定表空间对应的数据文件,注意:文件名以 .dbf 结尾。
size :后定义的是表空间的初始大小。
autoextend on :自动增长,当表空间存储都占满时,自动增长。
next :后指定的是一次自动增长的大小。
drop tablespace jiangqi;
-- 创建用户
create user jiangqi
identified by jiangqi
default tablespace jiangqi;
注意:
user :后定义的是用户名
identified by :后指定的是该用户的密码
default tablespace:指定用户的出生地,即所在的空间表
Oracle 数据库中常用角色
给jiangqi 用户授予dba 角色
grant dba to jiangqi;
注意:
在创建用户以后,必须给用户授权,否则用户无法登录。
序号 | 数据类型 | 描述 |
---|---|---|
1 | char(n) | 表示一个定长字符串,n 代表长度 |
2 | varchar,varchar2 | 表示一个可变字符串 |
3 | number | number(n) 表示一个整数,长度是 n |
number(m,n) 表示一个小数,总长度是 m ,小数是 n ,整数是 m-n | ||
4 | data | 表示日期类型,精确到年月日时分秒 |
5 | clob | 大对象,表示大文本数据类型,可存 4G (例:图书馆里所有的书) |
6 | blob | 大对象,表示二进制数据,可存 4G(例:很大的视频用这个类型) |
SQL 语句主要可以划分为以下 3 个类别
DDL(Data Definition Languages)语句:数据定义语言,这些语句定义了不同的数据段、数据库、表、列、索引等数据库对象。常用的语句关键字主要包括 create、drop、alter 等。
DML(Data Manipulation Languages)语句:数据操纵语句,用于添加、删除、更新和查询数据库记录,并检查数据完整性。常用的语句关键字主要包括 insert、delete、update 和 select 等。
DCL(Data Control Language)语句:数据控制语句,用于控制不同数据段直接的许可和访问级别的语句,这些语句定义了数据库、表、字段、用户的访问权限和安全级别,主要的语句关键字包括 grant、revoke 等。
DDL 是数据定于语言的缩写,简单来说,就是对数据库内部的对象进行创建、删除、修改等操作的语言。它和 DML 语言的最大区别是 DML 只是对表内部数据操作,而不涉及表的定义,结构的修改,更不会涉及其他对象。DDL 语句更多的由数据库管理员(DBA)使用,开发人员一般很少使用。
DDL(data definition language):DDL 比 DML 要多,主要的命令有 CREATE、ALTER、DROP 等,DDL 主要是用在定义或改变表(TABLE)的结构,数据类型,表之间的链接和约束等初始化工作上,他们大多在建立表时使用。
DDL(Data Definition Language,数据定义语言): 用于定义数据的结构,比如 创建、修改或者删除数据库对象。
DDL 包括:DDL 语句可以用于创建用户和重建数据库对象。下面是 DDL 命令:
-- 创建一个 person 表
create table person(
pid number(20),
pname varchar2(10)
);
修改表名
rename 原表名 to 新表名
rename person to person_new;
修改列名
alter table 表名 rename column 列名 to 新列名
alter table person rename column gender to sex;
修改字段类型
alter table 表名 modify(字段 类型)
-- 修改一列
alter table person modify gender char(1);
-- 修改多列
alter table person modify (gender char(1),pname char(5));
注意:
单列直接写,多列用括号括起来,中间用逗号隔开
添加列
alter table 表名 add 字段 类型
-- 添加一列
alter table person add gender number(1);
-- 添加多列
alter table person add (gender number(1),age number(3));
删除列
alter table 表名 drop column 字段
alter table person drop column sex;
删除表
drop table 表名;
drop table person;
DML (Data ManipulationLanguage数据操控语言)用于操作数据库对象中包含的数据,也就是说操作的单位是记录。
Oracle数据库的 DML表数据的操作有三种:
在很多数据库中都存在一个自动增长的列,如果现在要想在oracle中完成自动增长的功能,则只能依靠序列完成,所有的自动增长操作,需要用户手动完成处理。
-- 语法 :
CREATE SEQUENCE 序列名
[INCREMENT BY n]
[START WITH n]
[{MAXVALUE/ MINVALUE n|NOMAXVALUE}]
[{CYCLE |NOCYCLE}]
[{CACHE n|NOCACHE}];
create sequence s_person;
序列创建完成之后,所有的自动增长应该由用户自己处理,所以在序列中提供了以下的两种操作:
nextval :取得序列的下一个内容
currval :取得序列的当前内容
select s_person.nextval from dual;
select s_person.currval from dual;
注意:
普通插入
insert into person (pid,pname) values (1,'小明');
commit;
在插入数据时需要自增的主键
insert into person (pid,pname) values (s_person.nextval,'小明');
commit;
注意:
在使用pl/sql工具时,凡增删改都要加上 commit 语句,防止出现脏数据的情况。
update person set pname = '小马' where pid = 1;
commit;
删除表中全部记录
delete from person;
commit;
删除表结构
drop table person;
commit;
先删除表,再创建表。效果等同于删除表中全部记录
truncate table person;
commit;
注意:
在数据量大的情况下,尤其是在表中带有索引的情况下,该操作效率高。
索引可以提高查询效率,但是会影响增删改的效率。
system、sys、scott,其中system和sys的区别在与能否创建数据库,sys用户登录才可以创建数据库,而scott是给初学者学习的用户,学习者可以用Scott登录系统,注意scott用户登录后,就可以使用Oracle提供的数据库和数据表,这些都是oracle提供的,学习者不需要自己创建数据库和数据表,直接使用这些数据库和数据表练习SQL。
注意默认情况,scott用户是被锁住的,所以我们要先以sys用户登录,然后进行解锁,才可以哦
--scott用户,默认密码是tiger
--解锁scott用户(安装时若使用默认情况没有解锁和设置密码进行下列操作,要超级管理员(即dba权限)操作)
alter user scott account unlock;
--解锁scott用户的密码【此句也可以用来重置密码】
alter user scott identified by tiger;
字段分别是:员工编号、员工姓名、员工工作、员工直属领导编号、员工入职日期、员工工资、员工一年奖金、员工部门编号,其中DEPTNO是外键,与DEPT表的主键关联,如图1-2所示。
字段分别是:部门编号、部门名称、部门地址,如图1-3所示。
字段分别是:工资等级、最低工资、最高工资,如图1-4所示。
插入数据所使用的表,如图1-5所示。
以上四张表,我们主要使用前三张表来学习查询语句。
函数分为系统内置函数 ,自定义函数;了解系统内置函数(方法),重点掌握 to_date,to_char (字符和日期的转换)
根据函数的返回结果,我们将函数分为单行函数和多行函数
单行函数 : 作用于一行,返回一个值。
多行函数 : 作用于多行,返回一个值。
作用于一行,返回一个值。
select concat('jiangqi','text') from dual;
initcap : 首字母大写
select initcap('jiangqi') from dual;
lower/upper :把字符转换成小/大写
select upper('yes') from dual; --如图1-8
select lower('YES') from dual; --如图1-9
instr :找出某个字符的位置
instr(‘源字符串’,‘目标字符串’ ,‘开始位置’, ‘第几次出现’)
select instr('jiangqi','i',1,1) from dual; --如图1-10
select instr('jiangqi','i',1,2) from dual; --如图1-11
length/ lengthb:字符串的长度 (返回以字符为单位的长度/返回以字节为单位的长度)
select length('jiangqi') from dual;--如图1-12
select length('江七') from dual;--如图1-13
select lengthb('jiangqi') from dual;--如图1-12
select lengthb('江七') from dual;--如图1-14
总结:当字符串是中文时,得到的值与length得到的值不同。
lpad/rpad:用指定的字符在左侧/右侧填充
select lpad('text', 6, '*') from dual; --如图1-15
select rpad('text', 6, '*') from dual; --如图1-16
ltrim/rtrim:把左边/右边的指定字符剪掉
基础用法
rtrim(string); --去除字符串右侧空格
ltrim(string); -去除字符串左侧空格
进阶用法
ltrim(x,y)/rtrim(x,y) 函数是按照y中的字符一个一个截掉x中的字符,并且是从左/ 右边开始执行的,只要遇到y中有的字符, x中的字符都会被截掉, 直到在x的字符中遇到y中没 有的字符为止函数命令才结束
select ltrim('10900094323','109') from dual; --如图1-17
select ltrim('abcdabababe','ab') from dual; --如图1-18
select rtrim('abcdabababe','be') from dual; --如图1-19
select rtrim('10900094323','23') from dual; --如图1-20
trim:剪掉左右两边的指定字符
普通用法
trim(string); --去除字符串首尾的空格
进阶用法
select trim('a' from 'abcddcba') from dual;
select trim(leading 'a' from 'abcddcba') from dual;
select trim(trailing 'a' from 'abcddcba') from dual;
select trim(both 'a' from 'abcddcba') from dual; -- 以上结果如图1-21所示
replace:替换字符
replace( char, search_string, [replace_string]) 如果没有指定replace_string 变量的值,那么当发现search_string 变量的值时,就将其删除。输入可以为任何字符数据类型——CHAR、VARCHAR2、NCHAR、NVARCHAR2、CLOB或NCLOB。
select replace('abcdab','cd','ef') from dual; --如图1-22
select replace('abcdab','cd') from dual; --如图1-23
substr:截取字符串
substr(string string, int a, int b);
substr(string string, int a) ;
a 可以理解为从第a个字符开始截取后面所有的字符串。
select substr('HelloWorld',0,3) value from dual;
select substr('HelloWorld',1,3) value from dual;
select substr('Hello World',5,3) value from dual;
select substr('HelloWorld',-1,3) value from dual;
select substr('HelloWorld',-3,3) value from dual;
select substr('HelloWorld',3) value from dual; --以上结果如图1-24所示
注意:
mod :求模
**mod(m,n),**其中m和n都是数字
该函数的的规则是:返回m/n的余数,如果 n 为 0,则返回 m
select mod(10, 3) mod from dual;
select mod(10, 0) mod from dual; --以上结果如图1-25所示
round:四舍五入
round(number [,decimals]): number 待做截取处理的数值,decimals 指明需保留小数点后面的位数。可选项,忽略它则截去所有的小数部分,并四舍五入。如果为负数则表示从小数点开始左边的位数,相应整数数字用0填充,小数被去掉。需要注意的是,和trunc函数不同,对截取的数字要四舍五入。
select round(1234.5678) round from dual;
select round(1234.5678,1) round from dual;
select round(1234.5678,-2) round from dual;
select round(1234.5678,-4) round from dual; --以上结果如图1-26所示
trunc:截取数值
用于截取时间或者数值,返回指定的值。
日期处理:
**trunc(date [,fmt]):**date 为必要参数,是输入的一个date日期值,fmt 参数可忽略,是日期格式,缺省时表示指定日期的0点。
select trunc(to_date('2021-07-19 1:00:00','YYYY-MM-DD HH:MI:SS'),'yyyy') from dual ;--返回当年第一天
select trunc(to_date('2021-07-19 1:00:00','YYYY-MM-DD HH:MI:SS'),'mm') from dual ; --返回当月第一天
select trunc(to_date('2021-07-19 1:00:00','YYYY-MM-DD HH:MI:SS'),'dd') from dual ;--返回当前年月
select trunc(to_date('2021-07-19 1:00:00','YYYY-MM-DD HH:MI:SS'),'d') from dual ; --返回当前星期的第一天(星期日)
select trunc(to_date('2021-07-19 1:12:12','YYYY-MM-DD HH:MI:SS'),'hh') from dual ;--返回当前日期截取到小时,分秒补0
select trunc(to_date('2021-07-19 1:12:12','YYYY-MM-DD HH:MI:SS'),'mi') from dual ;--返回当前日期截取到分,秒补0 --以上结果如图1-27所示。
**注意:此图为连接结果(union)**后的图(下图皆为如此)。
数值处理:
trunc(number [,decimals]) :number 为必要参数,是输入的一个number数值,decimals 参数可忽略,是要截取的位数,缺省时表示截掉小数点后边的值。
select trunc(122.555) from dual t; --默认取整
select trunc(122.555,2) from dual t;
select trunc(122.555,-2) from dual t;--负数表示从小数点左边开始截取2位
--以上结果如图1-28所示。
ceil:向上取整
ceil(n) :取大于等于数值n的最小整数;
select ceil(9.5) from dual;
select ceil(-2.3) from dual; -- 以上结果如图1-29所示。
floor:向下取整
floor(n) :取小于等于数值n的最大整数
select floor(9.5) from dual;
select floor(-2.3) from dual;--以上结果如图1-30所示。
sysdate
用来得到系统的当前日期(如图1-31所示。)
select sysdate from dual;
add_months
增加或减去月份
select add_months(sysdate, 1) from dual;
select add_months(sysdate, -1) from dual;--以上结果如图1-32所示。
last_day
返回日期的最后一天(如图1-33所示)
select last_day(sysdate) from dual;
months_between(date2,date1)
给出date2-date1的月份
select months_between(to_date('2021-7-22','yyyy-mm-dd'), to_date('2020-7-22','yyyy-mm-dd')) months from dual union all
select months_between(to_date('2020-7-22','yyyy-mm-dd'), to_date('2021-7-22','yyyy-mm-dd')) months from dual union all
select months_between(to_date('2021-7-25','yyyy-mm-dd'), to_date('2020-7-22','yyyy-mm-dd')) months from dual union all
select months_between(to_date('2020-7-25','yyyy-mm-dd'), to_date('2021-7-22','yyyy-mm-dd')) months from dual;--以上结果如图1-34所示。
注意:
例子:
-- 查询出 emp 表中所有员工入职距离现在几天
select sysdate-e.hiredate from emp e;
-- 算出明天时刻
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;
to_char(date,‘format’)
把对应的数据转换为字符串类型
select to_char(sysdate, 'yyyy-mm-dd hh:mi:ss') from dual;
select to_char(sysdate, 'yyyy-mm-dd hh24:mi:ss') from dual;
select to_char(sysdate, 'fm yyyy-mm-dd hh24:mi:ss') from dual;
select to_char(122323.45, '$99999999.99') from dual;
--以上对比结果如图1-35所示。
-- Q 季度,1~3月为第一季度
select to_char(sysdate, 'q') from dual;-- 3 表示第三季度
-- MM 月份数
select to_char(sysdate, 'mm') from dual;--07表示7月
-- RM 月份的罗马表示
select to_char(sysdate, 'rm') from dual;--vii表示7月
-- Month 用9个字符长度表示的月份名
select to_char(sysdate, 'month') from dual;-- 7月
-- WW 当年第几周
select to_char(sysdate, 'ww') from dual;-- 29表示2021年7月22日为第29周
-- W 本月第几周
select to_char(sysdate, 'w') from dual;-- 2021年7月22日为为第4周
-- DDD 当年第几天. 1月1日为001,2月1日为032
select to_char(sysdate, 'ddd') from dual;
-- DD 当月第几天
select to_char(sysdate, 'dd') from dual;
-- D 周内第几天
select to_char(sysdate, 'd') from dual;-- 5 表示第五天(从星期天开始算)
-- DY 周内第几天缩写
select to_char(sysdate, 'dy') from dual;-- 星期四
-- HH或HH12 12进制小时数
select to_char(sysdate, 'hh') from dual;
-- HH24 24小时制
select to_char(sysdate, 'hh24') from dual;
-- MI 分钟数(0~59)
select to_char(sysdate, 'mi') from dual;
-- 提示注意不要将MM格式用于分钟(分钟应该使用MI)。MM是用于月份的格式,将它用于分钟也能工作,但结果是错误的。
-- SS 秒数(0~59)
select to_char(sysdate, 'ss') from dual;
to_date(string,‘format’)
将字符串转化为Oracle中的一个日期
select to_date('2011-03-24', 'yyyy/mm/dd') from dual;
select to_date(' 2021-7-22 16:35:10', 'fm yyyy-mm-dd hh24:mi:ss')from dual;
--以上结果如图1-36所示。
to_number(char)
将给出的字符转换为数字
select to_number('2021') as Year from dual
nvl(exp1,exp2)
如果oracle第一个参数为空那么显示第二个参数的值,如果第一个参数的值不为空,则显示第一个参数本来的值。
select nvl(e.comm,0) from emp e;
nullif(exp1,exp2)
如果表达式 exp1 与 exp2 的值相等则返回 null,否则,返回 exp1 的值
select e.comm,nullif(e.comm,300) from emp e;
nvl2(e1, e2, e3)
如果e1为NULL,则函数返回e3 ,若e1不为null,则返回e2。
select e.comm,e.sal,nvl2(e.comm,e.comm+e.sal,e.sal) from emp e;
coalesce()
依次考察各参数表达式,遇到非 null 值即停止并返回该值。
此表达式的功能为返回第一个不为空的表达式,如果都为空则返回空值。注意:所有表达式必须为同一类型或者能转换成同一类型。
select empno, ename, sal, comm, coalesce(sal+comm, sal, 0)总收入 from emp;
case when**(条件表达式的通用写法,mysql和oracle通用)**
case when用于SQL语句中的条件判断
case when 在Oracle 中的用法:
(a)已case开头,已end 结尾;
(b)分支中when后跟条件,then 显示结果;
(c)else 为除此之外的默认情况,类似于高级语言程序中的 switc case 的default可以不加;
(d)end后面跟别名;
case 有两种表达式:
(A)简单case表达式试用表达式确定返回值
-- 简单Case函数
-- 给 emp 员工起个中文名字
select e.ename,
case e.ename
when 'SMITH' then '史密斯'
when 'ALLEN' then '艾伦'
else '无名'
end "中文名"
from emp e; -- 结果如图1-41所示。
(B)搜索case表达式,使用条件确定返回值;
-- Case搜索函数
-- 判断表中员工工资,如果高于3000为高收入,
-- 高于1500低于3000为中收入,其余显示低收入
select e.sal,
case
when e.sal>3000 then '高收入'
when e.sal>1500 then '中收入'
else '低收入'
end "收入等级"
from emp e; -- 结果如图1-42所示。
decode (oracle 专用条件表达式)
decode判断字符串是否一样
语法:decode(value,if1,then1,if2,then2,if3,then3,...,else)
含义为
if 条件=值1 then
return(value 1)
else if 条件=值2 then
return(value 2)
…
else if 条件=值n then
return(value 3)
else
return(default)
end
select e.ename,
decode( e.ename,
'SMITH', '史密斯',
'ALLEN','艾伦',
'无名') "中文名"
from emp e; -- 结果如图1-41所示。decode相当于:case when then else end语句
decode判断大小
select decode(sign(var1-var2),-1,var1,var2) from dual;
sign()函数根据某个值是0、正数还是负数,分别返回0、1、-1
select decode(sign(100-90),-1,100,90) from dual;
select decode(sign(100-90),0,100,90) from dual;
select decode(sign(100-90),1,100,90) from dual;
-- 以上结果如图1-43所示。
使用decode函数分段
-- 判断表中员工工资,如果高于3000为高收入,
-- 高于1500低于3000为中收入,其余显示低收入
select e.sal,
decode(sign(e.sal-3000),
1,'高收入',
0,'高收入',
-1,decode(sign(e.sal-1500),
1,'中收入',
0,'中收入',
-1,'低收入'
)) "收入等级"
from emp e; -- 结果如图1-42所示。
多行函数又叫聚合函数,也叫组函数,作用于多行,返回一个值。
聚合函数的特性: 可以把多行记录变成一个值。
聚合函数有 : count、sum、max/min、avg
--求员工的工资总和
select sum(sal) from emp;
--求个数(统计数量)
select count(*) from emp;
--求平均工资
select sum(sal)/count(*) 方式一, avg(sal) 方式二 from emp;
--关于空值:聚合函数会自动滤空
select count(*), count(comm) from emp;
--max和min:求最高工资和最低工资
select max(sal) 最高工资,min(sal) 最低工资 from emp;
分组查询中,出现在group by后面的原始列,才能出现在select后面,没有出现在group by后面的列,想在select后面,必须加上聚合函数。
求各个部门的平均工资
select e.deptno,avg(e.sal)
from emp e
group by e.deptno;
求每个部门的平均工资大于2000的部门
select deptno,avg(sal)
from emp
group by deptno
having avg(sal)>2000;
求每个部门的工资高于800的员工的平均工资并且按照部门编号从小到大排列
select e.deptno,avg(e.sal)
from emp e
where e.sal>800
group by e.deptno
order by e.deptno; -- 默认从小到大排列,从大到小排列使用关键词 desc 在后面
求每个部门的工资高于800的员工的平均工资然后再查询平均工资大于2000的部门
select e.deptno,avg(e.sal)
from emp e
where e.sal>800
group by e.deptno
having avg(e.sal)>2000;
注意:
(A) where是过滤分组前的数据,having是过滤分组后的数据
(B) 表现形式:where 必须在 group by 之前,having 是在 group by 之后
按部门,不同的工种,统计平均工资
select deptno,job,avg(sal)
from emp
group by deptno,job;
注意:
group by作用于多列:先按照第一列分组;如果相同,再按照第二列分组
select查询语句执行顺序:
from 子句第一个执行。
where子句, 通过过滤条件排除不满足条件的记录
group by 子句, 对where过滤后的记录(满足条件的记录进行分组)
having 子句, 对分组后的记录进行过滤(分组条件)
select子句, 用过滤后的记录生产查询列表
order by 子句, 对所有生成的记录进行排序
从多个表中获取相应的数据。
笛卡尔积
连接条件无效或被省略,两个表的所有行都发生连接,所有行的组合都会返回(n*m)。
select * from emp e,dept d;
等值连接(内连接或简单连接)
两个表的连接条件的列值必须相等,通常这样的连接包含一个主键和一个外键。
-- 等值连接
select *
from emp e,dept de
where e.deptno= de.deptno;
--内连接
select *
from emp e inner join dept de
on e.deptno= de.deptno;
外连接
外部连接不只列出与连接条件向匹配的行,而是列出左表(左外连接时)、右表(右外连接时)或两个表(全外连接时(笛卡尔积))中所有符合搜索条件的数据行。
左外连接
查询出所有部门,以及部门下的员工信息
-- 通用左外连接
select *
from emp e left join dept de
on e.deptno= de.deptno;
-- Oracle 专用左外连接
select *
from emp e ,dept de
where e.deptno = de.deptno (+);
右外连接
查询所有员工信息,以及员工所属部门
-- 通用右外连接
select *
from emp e right join dept de
on e.deptno= de.deptno;
--Oracle 专用右外连接
select *
from emp e ,dept de
where e.deptno (+) = de.deptno ;
注意:
dept表为不缺乏连接信息的表,emp表为缺乏连接信息的表
外部连接运算符(+)放在缺少相关连接信息的表的一侧,它能返回该表中那些在另一个表中没有得到匹配的记录。
自连接
站在不同的角度把一张表看成多张表,并起多个别名,然后再根据要求进行连接。
查询出员工姓名,员工领导姓名
select e1.ename,e2.ename
from emp e1 ,emp e2
where e1.mgr = e2.empno;
查询出员工姓名,员工部门名称,员工领导姓名,员工领导部门名称
select e1.ename,d1.dname,e2.ename,d2.dname
from emp e1 ,emp e2,dept d1,dept d2
where e1.mgr = e2.empno
and e1.deptno=d1.deptno
and e2.deptno=d2.deptno;
在SQL语言中,由select-from-where组成的语句称为查询块。当需要获得一个未知的但又在数据库中存在的值时,将一个查询块嵌套在另一个查询块的where子句的条件中的查询块称为子查询。子查询中还可以嵌套子查询。子查询的结果可以是一个值也可以是多个值。
子查询 (内查询) 在主查询之前一次执行完成。
子查询的结果被主查询(外查询)使用。
查询出工资大于SCOTT的员工姓名
select ename
from emp
where sal > (select sal from emp where ename='SCOTT');
注意:
a. 子查询要包含在括号内,将子查询放在比较条件的右侧。
b. 单行操作符对应单行子查询,多行操作符对应多行子查询。
单行子查询
单行子查询,只返回一行,使用单行比较符(>, = ,<, >= ,<= ,!=, <>)
--子查询中使用聚合函数
select ename,sal
from emp
where sal=(select min(sal) from emp);
--子查询中的having子句
--首先执行子查询
--向主查询中的having子句返回结果
select deptno, min(sal)
from emp
group by deptno
having min(sal) > (select min(sal) from emp);
多行子查询
多行子查询,返回多行,使用多行比较符(IN, ANY ,ALL)
返回一个集合
--查询出工资比部门10里任意一个人工资高的员工信息
select *
from emp
where sal > any (select sal from emp where deptno = 10);
--查询出工资和部门10里任意一样的员工信息
select *
from emp
where sal in (select sal from emp where deptno = 10);
--查询不是老板的员工信息
select *
from emp
where empno not in(select mgr from emp);
返回一个表
-- 查询出每个部门最低工资,和最低工资员工姓名,和该员工所在部门名称
-- 1,先查询出每个部门最低工资
select deptno,min(sal) msal
from emp
group by deptno
-- 2,三表联查,得到最终结果
select t.deptno,t.msal,e.ename,d.dname
from (select deptno,min(sal) 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表工资倒叙排列后,每页五条记录,查询第二页。
select rownum,e.*
from emp e
order by e.sal desc
注意:排序操作会影响rownum的顺序,故如果涉及到排序,但是还要使用rownum的话,我们可以再次嵌套查询来消除影响。
select rownum ,t.*
from ( select rownum,e.*
from emp e
order by e.sal desc) t;
select rownum ,e.*
from ( select rownum,e.*
from emp e
order by e.sal desc) e
where rownum < 11 and rownum >5;
由图可得出:rownum 行号不能写上大于一个正数。
最后正确结果为:(也是分页查询的固定模板,其中数字为参数传进去的)
--emp表工资倒叙排列后,每页五条记录,查询第二页。
select *
from(
select rownum rn ,e.*
from ( select rownum,e.*
from emp e
order by e.sal desc) e
where rownum < 11)
where rn > 5;
视图是一个虚拟表,视图并不在数据库中存储数据值,数据库中只在数据字典中存储对视图的定义。
视图是从一个或多个实际表中获得的,这些表的数据存放在数据库中。那些用于产生视图的表叫做该视图的基表。一个视图也可以从另一个视图中产生。
总结:
视图就是提供一个查询的窗口,所有数据来源于原表。
创建视图必须有 dba 权限
create [or replace] [{force|noforce}] view view_name
as
select 查询
[with read only]
**or replace :**如果视图已经存在,则替换旧视图。
**force :**即使基表不存在,也可以创建该视图,但是该视图不能正常使用,当基表创建成功后,视图才能正常使用。
**noforce :**如果基表不存在,无法创建视图,该项是默认选项。
**with read only :**默认可以通过视图对基表执行增删改操作,但是有很多在基表上的限制(比如:基表中某列不能为空,但是该列没有出现在视图中,则不能通过视图执行insert操作),with read only
说明视图是只读视图,不能通过该视图进行增删改操作。现实开发中,基本上不通过视图对表中的数据进行增删改操作。
代码演示
-- 查询语句创建表
create table emp as select * from scott.emp;
select * from emp;
-- 创建视图【必须要有 dba 权限】
create view v_emp as select ename,job from emp;
-- 修改视图【不推荐】 视图里改变数据改的还是原表的数据
update v_emp set job='CLERK' where ename='ALLEN';
commit;
-- 创建只读视图
create view v_emp1 as select ename,job from emp with read only;
注意:修改视图不推荐使用,因为视图里改变数据改变的是原表中的数据。
1)索引是数据库对象之一,用于加快数据的检索,类似于书籍的索引。在数据库中索引可以减少数据库程序查询结果时需要读取的数据量,类似于在书籍中我们利用索引可以不用翻阅整本书即可找到想要的信息。
2)索引是建立在表上的可选对象;索引的关键在于通过一组排序后的索引键来取代默认的全表扫描检索方式,从而提高检索效率
3)索引在逻辑上和物理上都与相关的表和数据无关,当创建或者删除一个索引时,不会影响基本的表;
4)索引一旦建立,在表上进行DML操作时(例如在执行插入、修改或者删除相关操作时),oracle会自动管理索引,索引删除,不会对表产生影响
5)索引对用户是透明的,无论表上是否有索引,sql语句的用法不变
6)oracle创建主键时会自动在该列上创建索引
总结:
- 索引就是在表的列上构建一个二叉树。
- 会大幅度提高查询效率的目的,但是索引会影响增删改的效率
单列索引
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='xx'; -- 触发复合索引
select * from emp where ename='SCOTT' or job='xx'; -- 不触发索引
select * from emp where ename='SCOTT'; -- 触发单列索引
pl/sql编程语言是对sql语言的扩展,使得sql语言具有过程化编程的特性;
pl/sql编程语言比一般的过程化编程语言,更加灵活高效;
pl/sql编程语言主要用来编写存储过程和存储函数等。
-- 声明方法
declare
i number(2) :=10;
s varchar2(10) :='小明';
ena emp.ename%type; -- 定义emp中ename属性的类型(引用型变量)
emprow emp%rowtype; -- 记录型变量
begin
dbms_output.put_line(i);-- 输出语句
dbms_output.put_line(s);
select ename into ena from emp where empno=7788; -- select赋值给ena(into)
dbms_output.put_line(ena);
select * into emprow from emp where empno=7788;
dbms_output.put_line(emprow.ename || '的工作为'||emprow.job);
end;
注意:
-- pl/ sql 中的if判晰
-- 输入小于18的数字,输出未成年
-- 输入大于18小于40的数字,输出中年人
-- 输入大于40的数字,输出老年人
declare
i number(3) :=ⅈ -- & 代表输入;后面变量随便写
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 if 作为 if 结束标志
end;
注意:
end if
结束 if用三种方式输出1到10 的数字
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;
注意:
使用PL/SQL退出循环时,必须遵循以下步骤。
declare
begin
for i in 1..10
loop
dbms_output.put_line(i);
end loop;
end;
注意:
in
后加上reverse
关键字,并且循环上边界和下边界的顺序无需改变概念
游标:指向查询结果集的指针,指向哪一行,提取哪一行的数据(PLSQL的游标默认指向结果集的第一行)
显示游标的四个属性:
隐式游标:固定名称sql
隐式游标的四个属性
必须在事务结束之前读取游标属性,而且只能读取最近的一次DML(数据操作语言)操作的游标状态
使用步骤
使用游标分为4个步骤
案例分析
输出emp 表中所有员工的姓名
declare
cursor c1 is select * from emp;
emprow emp%rowtype;
begin
open c1;
loop
fetch c1 into emprow;
exit when c1%notfound;
dbms_output.put_line(emprow.ename);
end loop;
close c1;
end;
给指定部门员工涨工资
declare
cursor c2(eno emp.deptno%type)
is select empno from emp where deptno=eno;
en emp.empno%type;
begin
open c2(10);
loop
fetch c2 into en;
exit when c2%notfound;
update emp set sal=sal+100 where empno=en;
commit;
end loop;
end;
注意:
open c2(10);
存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象,任何一个设计良好的数据库应用程序都应该用到存储过程。
简单来说:存储过程就是提前已经编译好的一段pl/sql 语言,放置在数据库端可以直接调用。这一段pl/sql 一般都是固定步骤的业务。
创建存储过程的语法
create [or replace] procedure 过程名[(参数名in/out数据类型)]
as
变量1 类型(值范围);
变量2 类型(值范围);
begin
PLSQL子程序体;
end;
或者
create [or replace] procedure 过程名[(参数名in/out数据类型)]
is
变量1 类型(值范围);
变量2 类型(值范围);
begin
PLSQL子程序体;
end;
案例分析
不带参数的
-- 打印“hello world”
create or replace procedure p_hello is
--如果需要,在此处声明变量
begin
dbms_output.put_line('hello world');
end p_hello;
-- 上面代码执行后,再执行下面测试代码
--测试
begin
-- Call the procedure
p_hello;
end; --如图3-1所示。
带输入参数的
--查询并打印某个员工的姓名,薪水,要求调用时传入员工编号,控制台自动打印
create or replace procedure p_empInfo(i_empno IN emp.empno%TYPE)
as
-- 声明变量
v_ename emp.ename%TYPE;
v_sal emp.sal%TYPE;
begin
select ename, sal into v_ename, v_sal from emp where empno = i_empno;
dbms_output.put_line('员工编号:' || i_empno || ',姓名:' || v_ename || ',薪水:' || v_sal);
end p_empInfo;
-- 上面代码执行后,再执行下面测试代码
-- 测试,调用带入参存储过程
begin
-- Call the procedure
p_empInfo(7788);
p_empInfo(7902);
end; --如图3-2所示。
带输入输出参数(返回值)的
--输入员工号查询某个员工(7788)信息,要求将年薪作为返回值输出,给调用的程序使用
create or replace procedure p_empInfo2(eno in emp.empno%TYPE, year_sal out number)
as
s number(10);
pcomm emp.comm%type;
begin
select sal*12 ,nvl(comm,0) into s,pcomm from emp where empno = eno;
year_sal :=s+pcomm;
end;
-- 上面代码执行后,再执行下面测试代码
--测试,测试窗口中调用该存储过程,需要事先定义一个变量作为 存储过程返回值的接收参数,在打印语句中打印该参数的值,
declare
yearsal number(10);
begin
p_empInfo2(7788,yearsal);
dbms_output.put_line(yearsal);
end;
注意:
select … into …
给变量赋值;into
查询语句赋值或者:=
赋值操作的参数,都必须使用out
来修饰。函数(function)为一命名的存储程序,可带参数,并返回一计算值。函数和过程的结构类似,但必须有一个return子句,用于返回函数值。函数说明要指定函数名、结果值的类型,以及参数类型等。
语法
create or replace function 函数名(Name in type, Name in type,.)
return 数据类型
is/as 结果变量 数据类型;
begin
return(结果变量);
end 函数名;
注意:
存储过程和存储函数的区别
案例分析
--查询某个员工的年收入
create or replace function queryempincome(eno in number)
return number
as
--月薪和奖金
psal emp.sal%type;
pcomm emp.comm%type;
begin
select sal,comm into psal,pcomm from emp where empno=eno;
--返回年收入
return psal*12+nvl(pcomm,0);
end;
-- 上面代码执行后,再执行下面测试代码
--测试
-- 存储函数再调用的时候,返回值需要接收
declare
s number(10);
begin
s:=queryempincome(7788);
dbms_output.put_line(s);
end;
注意: 存储函数在调用的时候,返回值必须需要接收,否则执行错误。
语法区别:
本质区别:存储函数有返回值,而存储过程没有返回值。
本质区别表现形式案例
我们可以使用存储函数有返回值的特性,用来自定义函数。而存储过程不能用来自定义函数。
案例需求:查询出员工姓名,员工所在部门名称。
-- 案例准备工作:把scott用户下的dept表复制到当前用户下
create table dept as select * from scott.dept;
-- 使用传统方式来实现案例需求
select e.ename,d.dname
from emp e,dept d
where e.deptno=d.deptno;
-- 使用存储函数来实现提供一个部门编号,输出一个部门名称
create or replace function fdna(dno dept.deptno%type)
return dept.dname%type
is
dna dept.dname%type;
begin
select dname into dna from dept where deptno=dno;
return dna;
end;
-- 使用 fdna 存储函数来实现案例需求:查询出员工姓名,员工所在部门名称。
select e.ename,fdna(e.deptno)
from emp e;
注意:上述过程,存储过程不能来做,因为存储过程没有返回值
触发器,就是制定一个规则,在我们做增删改操作的时候,只要满足该规则,自动触发,无需调用。|
触发器事件:insert, update, insert
触发时间: after, before
触发参数: :old 和 :new
触发语句 | :old | :new |
---|---|---|
insert | 所有字段都是空的(null) | 将要插入的数据 |
update | 更新以前该行的值 | 更新后的值 |
delete | 删除以前该行的值 | 所有字段都是空的(null) |
触发层级:行级触发器,语句级触发器
for each row
for each row
注意:加for each row
是为了使用:old
或者:new
对象或者一行记录。
其他
raise_application_error('-20000','错误提示信息')
; 异常值范围: -20999到-20000
案例:插入一条记录,输出一个新员工入职
create or replace trigger t1
after
insert
on person
declare
begin
dbms_output.put_line('一个新员工入职');
end;
-- 触发t1
insert into person values(1,'小红');
commit; -- 如图3-3所示。
案例一:不能给员工降薪
create or replace trigger t2
before
update
on emp
for each row
declare
begin
if :old.sal>:new.sal then
raise_application_error(-20001,'不能给员工降薪');
end if;
end;
-- 触发t2
update emp set sal=sal-1 where empno=7788;
commit; -- 如图3-4所示。
案例二:触发器实现主键自增
分析:在用户做插入操作之前,拿到即将插入的数据,给该数据中的主键列赋值
create or replace trigger auid
before
insert
on person
for each row
declare
begin
select s_person.nextval into :new.pid from dual;
end;
-- 查询person表数据
select * from person; -- 如图3-5所示。
-- 使用auid 实现主键自增
insert into person(pname) values('a');
commit; -- 连续执行四次
select * from person; -- 如图3-6所示。
insert into person values(1,'b');
commit; -- 连续执行两次
select * from person; -- 如图3-7所示。
总结: insert into person values(1,'b');
不论序号是什么数字,其pid都是按照序列的顺序依次递增1。
我的Oracle版本是11g ,故需要oracle6_g.jar。
创建maven工程
导入jar包
<dependencies>
<dependency>
<groupId>com.oracle.database.jdbc.debuggroupId>
<artifactId>ojdbc6_gartifactId>
<version>11.2.0.4version>
<scope>runtimescope>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.1version>
<scope>testscope>
dependency>
dependencies>
public class OracleDemo {
@Test
public void javaCallOracle() throws Exception {
// 加载驱动
Class.forName("oracle.jdbc.driver.OracleDriver");
//得到Connection连接
Connection connection= DriverManager.getConnection("jdbc:oracle:thin:localhost:orcl","jiangqi","jiangqi");
// 得到预编译的Statement对象
PreparedStatement pstm = connection.prepareStatement("select * from emp where empno=?");
//给参数赋值,查询7788员工的信息
pstm.setObject(1,7788);
//执行数据库查询操作
ResultSet resultSet = pstm.executeQuery();
//输出结果
while (resultSet.next()){
// 输出7788员工姓名信息
System.out.println(resultSet.getString("ename"));
}
//释放资源
resultSet.close();
pstm.close();
connection.close();
}
}
CallableStatement:用于执行_SL存储过程的接口。
JDBC API 提供了一个存储过程SQL转义语法,该语法允许对所有RDBMS使用标准方式调用存储过程。
此转义语法有一个包含结果参数的形式和一个不包含结果参数的形式。
如果使用结果参数,则必须将其注册为OUT参数。其他参数可用于输入、输出或同时用于二者。
参数是根据编号按顺序引用的,第一个参数的编号是1。
{call
调用存储过程使用
/**
* @Description: 调用存储过程
* @Author: 江七
* @Date: 2021/7/26 17:39
* @return: void
**/
@Test
public void javaCallProcedure() throws Exception {
// 加载驱动
Class.forName("oracle.jdbc.driver.OracleDriver");
//得到Connection连接
Connection connection= DriverManager.getConnection("jdbc:oracle:thin:localhost:orcl","jiangqi","jiangqi");
// 得到预编译的Statement对象
CallableStatement pstm = connection.prepareCall("{call p_empInfo2(?,?)}");
//给参数赋值
pstm.setObject(1,7788);
pstm.registerOutParameter(2, OracleTypes.NUMBER);
//执行数据库查询操作
pstm.execute();
//输出结果[第二个参数]
System.out.println(pstm.getObject(2));
//释放资源
pstm.close();
connection.close();
}
{?=call}
调用存储函数使用
/**
* @Description: 调用存储函数
* @Author: 江七
* @Date: 2021/7/26 17:49
* @return: void
**/
@Test
public void javaCallFunction() throws Exception {
// 加载驱动
Class.forName("oracle.jdbc.driver.OracleDriver");
//得到Connection连接
Connection connection= DriverManager.getConnection("jdbc:oracle:thin:localhost:orcl","jiangqi","jiangqi");
// 得到预编译的Statement对象
CallableStatement pstm = connection.prepareCall("{?=call queryempincome(?)}");
//给参数赋值
pstm.setObject(2,7788);
pstm.registerOutParameter(1, OracleTypes.NUMBER);
//执行数据库查询操作
pstm.execute();
//输出结果[第一个参数],第一个参数是返回值
System.out.println(pstm.getObject(1));
//释放资源
pstm.close();
connection.close();
}