一、 Oracle的安装(windowXP、win7、Linux)和卸载
1.1 Oracle的安装
1.1.1 在WindowsXP、Win7下安装
第一:解压win32_11gR2_database_1of2、win32_11gR2_database_2of2,生成detabase目录
第二:安装oracle
A、点击setup图标即可,注意:安装目录不要含有中文
B、在弹出的第一个界面中取消更新选择项,点击下一步
C、在弹出的警告框中选择是
D、选择创建和配置数据库选项,下一步
E、选择桌面类安装,点击下一步
F、弹出的窗口中输入全局数据库名:orcl
输入管理口令:bluedot
默认的管理员是:sys和system
G、点完成,开始安装数据库,出现进度条
H、口令管理
I、设置口令
J、完成安装
1.2 Oracle的卸载:
1、 开始->设置->控制面板->管理工具->服务 停止所有Oracle服务。
2、 开始->程序->Oracle - OraHome81->Oracle Installation Products-> Universal Installer,单击“卸载产品”-“全部展开”,选中除“OraDb11g_home1”外的全部目录,删除。
3、 运行regedit,选择HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE,按del键删除这个入口。
4、 运行regedit,选择HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services,滚动这个列表,删除所有Oracle入口(以oracle或OraWeb开头的键)。
5、 运行refedit,HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application,删除所有Oracle入口。e
6、 删除HKEY_CLASSES_ROOT目录下所有以Ora、Oracle、Orcl或EnumOra为前缀的键。
7、 删除HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\MenuOrder\Start Menu\Programs中所有以oracle开头的键。
8、删除HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBCINST.INI中除Microsoft ODBC for Oracle注册表键以外的所有含有Oracle的键。
9、我的电脑–>属性–>高级–>环境变量,删除环境变量CLASSPATH和PATH中有关Oracle的设定。
10、从桌面上、STARTUP(启动)组、程序菜单中,删除所有有关Oracle的组和图标。
11、删除所有与Oracle相关的目录(如果删不掉,重启计算机后再删就可以了)包括:
1.C:\Program file\Oracle目录。
2.ORACLE_BASE目录(oracle的安装目录)。
3.C:\WINDOWS\system32\config\systemprofile\Oracle目录。
4.C:\Users\Administrator\Oracle或C:\Documents and Settings\Administrator\Oracle目录。
5.C:\WINDOWS下删除以下文件ORACLE.INI、oradim73.INI、oradim80.INI、oraodbc.ini等等。
6.C:\WINDOWS下的WIN.INI文件中若有[ORACLE]的标记段,删除该段。
12、如有必要,删除所有Oracle相关的ODBC的DSN
13、到事件查看器中,删除Oracle相关的日志 说明: 如果有个别DLL文件无法删除的情况,则不用理会,重新启动,开始新的安装,安装时,选择一个新的目录,则,安装完毕并重新启动后,老的目录及文件就可以删除掉了。
二、 用户管理
2.1 创建用户
注:创建用户只能在管理员下完成
CREATE USER 用户名 IDENTIFIED BY 密码。
|-CREATE USER demo IDENTIFIED BY 123456;
2.2 用户分类
|-管理员和普通用户
|-管理员
|-超级管理员:sys/bluedot
|-管理员:system/bluedot
|-普通用户:scott/tiger
hr/hr
|–常见角色:sysdba、sysoper
2.3 用户登录
2.3.1 在命令行窗口登录[c/s]
步骤:
运行 sqlplus /nolog
conn demo/123456
2.3.2 另外的一种登录方式【B/S】
输入网址—-https://localhost:1158/em
输入用户名密码进入主界面
2.4 修改用户密码
注:修改密码必须要在级别高的用户下进行修改
ALTER USER 用户名 IDENTIFIED BY 密码;
conn sys/bluedot as sysdba
ALTER USER demo IDENTIFIED BY 654321;
2.5 查询用户
2.5.1查看用户信息
1、SELECT * FROM DBA_USERS;——–查看所有用户的详细信息
2、SELECT * FROM ALL_USERS;——-查看所有用户简要信息
3、SELECT * FROM USER_USERS;————查看当前用户的所用信息
2.5.2 查看用户或角色系统权限(直接赋值给用户或角色的系统权限)
SELECT * FROM DBA_SYS_PRIVS;———-全部
SELECT * FROM USER_SYS_PRIVS; ———当前用户
2.5.3 查看角色(登录用户拥有的角色)所包含的的权限
SELECT * FROM ROLE_SYS_PRIVS;
2.5.4 查看用户对象权限
SELECT * FROM DBA_TAB_PRIVS;
SELECT * FROM ALL_TAB_PRIVS;
SELECT * FROM USER_TAB_PRIVS;
2.5.5 查看所有角色
SELECT * FROM DBA_ROLES;
SELECT * FROM DBA_ROLE_PRIVS;
SELECT * FROM USER_ROLE_PRIVS;
2.5.6 查看哪些用户有sysdba或sysoper系统权限(查询时需要相应权限)
SELECT * FROM V$PWFILE_USERS;
2.5.7 查看Oracle提供的系统权限
SELECT name FROM SYS.SYSTEM_PRIVSILEGE_MAP;
2.6 密码失效
提示用户第一次连接的时候需要修改密码,让用户的密码到期
|- ALTER USER 用户名 PASSWORD expire ;
2.7 授权
GRANT 权限/角色 TO 用户
给 demo 用户以创建 session 的权限:GRANT create session TO demo;
角色:————-角色就是一堆权限的集合
Create role myrole;
Grant create table to myrole;
Drop role myrole; 删除角色
1.CONNECT, RESOURCE, DBA
这些预定义角色主要是为了向后兼容。其主要是用于数据库管理。oracle建议用户自己设计数据库管理和安全的权限规划,而不要简单的使用这些预定角色。将来的版本中这些角色可能不会作为预定义角色。
2.DELETE_CATALOG_ROLE, EXECUTE_CATALOG_ROLE, SELECT_CATALOG_ROLE 这些角色主要用于访问数据字典视图和包。
3.EXP_FULL_DATABASE, IMP_FULL_DATABASE 这两个角色用于数据导入导出工具的使用。
GRANT 权限(select、update、insert、delete) ON schema.table TO 用户
|- GRANT select ON scott.emp TO test ;
|- Grant all on scott.emp to test; –将表相关的所有权限付给 test
|- Grant update(ename) on emp to test; 可以控制到列(还有 insert)
2.8 收权
REVOKE 权限/角色 ON schema.table FROM 用户
|- REVOKE select ON scott.emp FROM test ;
2.9 锁住一个用户
ALTER USER 用户名 ACCOUNT LOCK|UNLOCK
|- ALTER USER test ACCOUNT LOCK ;
|- ALTER USER test ACCOUNT UNLOCK ;
2.10 删除用户
|-DROP USER 用户名;
|-Drop user demo;
如果该用户下面已经存在表等一些数据库对象。则必须用级联删除
|-DROP USER 用户名 CASCADE;
|-Drop user demo cascade;
备注:帮助
help index
help conn ——–显示具体的
eidt—————进入编辑文档
三、 Oracle的体系结构
3.1 Oracle数据库的整体架构 (DBA)
┌──────────────────────────────┐
┌────┐ │ Instance │
│ User │ │ ┌──────────────────────────┐ │
│ process│ │ │ ┌────────┐ SGA │ │
└────┘ │ │ │ Shared Pool │ │ │
↓ │ │ │ ┌─────┐ │ ┌────┐ ┌────┐ │ │
↓ │ │ │ │Library │ │ │Database│ │ Redo │ │ │
┌────┐ │ │ │ │ Cache │ │ │Buffer │ │ Log │ │ │
│ Server │ │ │ │ └─────┘ │ │Cache │ │ Buffer │ │ │
│process │ │ │ │ ┌─────┐ │ │ │ │ │ │ │
└────┘ │ │ │ │Data Dict │ │ └────┘ └────┘ │ │
↓ │ │ │ │ Cache │ │ │ │
→→→→→→│ │ │ └─────┘ │ │ │
│ │ └────────┘ │ │
│ └──────────────────────────┘ │
│ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌───┐│
│ │PMON│ │SNON│ │DBWR│ │LGWR│ │CHPT│ │OTHERS││
│ └──┘ └──┘ └──┘ └──┘ └──┘ └───┘│
└──────────────────────────────┘
↑ ↓
┌─────┐ ┌───────────────────┐
│Parameter │ │┌───┐ ┌────┐ ┌────┐│
│ file │ ││Data │ │control │ │Redo Log││ ┌─────┐
└─────┘ ││files │ │ files │ │ files ││ │Archived │
┌─────┐ │└───┘ └────┘ └────┘│ │Log files │
│ Password │ │ Database │ └─────┘
│ file │ │ │
└─────┘ └───────────────────┘
由上图可知,Oracle数据库由实例和数据库组成。
3.2 数据库存储结构
3.2.1 数据库存储结构
Oracle数据库有物理结构和逻辑结构。数据库的物理结构是数据库中的操作系统文件的集合。数据库的物理结构由数据文件、控制文件和重做日志文件组成。
1、数据文件:数据文件是数据的存储仓库。
2、重做日志文件:重做日志文件包含对数据库所做的更改记录,在发生故障时能够恢复数据。重做日志按时间顺序存储应用于数据库的一连串的变更向量。其中仅包含重建(重做)所有已完成工作的最少限度信息。如果数据文件受损,则可以将这些变更向量应用于数据文件备份来重做工作,将它恢复到发生故障的那一刻前的状态。重做日志文件包含联机重做日志文件(对于连续的数据库操作时必须的)和归档日志文件(对于数据库操作是可选的,但对于时间点恢复是必须的)。
3、控制文件:控制文件包含维护和验证数据库完整性的必要的信息。控制文件虽小,但作用非常大。它包含指向数据库其余部分的指针:联机重做日志文件和数据文件的位置,以及更新的归档日志文件的位置。它还存储着维护数据库完整性所需的信息。控制文件不过数MB,却起着至关重要的作用。
除了三个必须的文件外数据库还能有其它非必须的文件如:参数文件、口令文件及归档日志文件。
1、实例参数文件:当启动oracle实例时,SGA结构会根据此参数文件的设置内置到内存,后台进程会据此启动。
2、口令文件:用户通过提交用户名和口令来建立会话。Oracle根据存储在数据字典的用户定义对用户名和口令进行验证。
3、归档重做日志文件:当重做日志文件满时将重做日志文件进行归档以便还原数据文件备份。
3.2.2 Oracle数据库结构的16个要点(表空间–>段–>区–>块)
1、一个数据文件只能归到某一个表空间上,每个表空间可以含一个或多个数据文件。包括系统数据和用户数据。
2、表空间是包括一个或多个数据文件的逻辑结构。用于存放数据库表、索引、回滚段等对象的磁盘逻辑空间
3、数据库文件是存放实际数据的物理文件。包括实例和数据库。
4、数据文件可以在创建表空间时创建,也可以以增加的方式创建。
5、数据文件的大小一般与操作系统限制有关。
6、控制文件是Oracle的重要文件,主要存放数据文件、日志文件和数据库的基本信息,一般在数据打开时访问。
7、日志文件在数据库活动时使用。
8、临时表空间是用于存放排序段的磁间;临时表空间由一个或多个临时文件组成。
9、归档日志文件由归档进程将联机日志文件读出并写到一个路径上的文件。
10、Oracle实例由一组后台进程和内存结构组成。
11、Oracle实例的内存结构常叫系统全局区,简称SGA。
12、DBA_开头的数据字典存放的字符信息都是大写,而V开头的视图存放的都是小写。 13、后台进程是一组完成不同功能的程序,主要包括DBWR、LGMR、CKPT等。 14、数据字典是Oracle的重要部分,也就是用于系统内部的一组表。 15、数据字典分为动态和静态两部分,静态主要是DBA开头的数据字典,而动态则是以V_开头的视图。
16、SGA分为数据缓冲区、共享池和日志缓冲区。
3.2.3 Oracle逻辑结构及表空间
1.ORACLE逻辑结构
ORACLE将数据逻辑地存放在表空间,物理地存放在数据文件中。
一个表空间任何一个时刻只能属于一个数据库。
数据库——表空间——段——区——ORACLE块
每个数据库由一个或多个表空间组成,至少一个。
每个表空间基于一个或多个操作系统的数据文件,至少一个,一个操作系统的数据文件只能属于一个表空间。一个表空间可以存放一个或多个段 segment。
每个段由一个或多个区段extent组成。
每个区段由一个或多个连续的ORACLE数据库块组成。
每个ORACLE数据块由一个或多个连续的操作系统数据块组成。
每个操作系统数据文件由一个或多个区段组成,由一个或多个操作系统数据块组成。
⑴、表空间(tablespace)
表空间是数据库中最大的逻辑单位,每一个表空间由一个或多个数据文件组成,一个数据文件只能与一个表空间相联系。每一个数据库都有一个SYSTEM表空间,该表空间是在数据库创建或数据库安装时自动创建的,用于存储系统的数据字典表,程序系统单元,过程函数,包和触发器等,也可用于存储用户数据表,索引对象。表空间具有在线(online)和离线(offline)属性,可以将除SYSTME以外的其他任何表空间置为离线。
⑵、段(segment)
数据库的段可以分为四类:数据段、索引段、回退段和临时段。
⑶、区
区是磁盘空间分配的最小单位。磁盘按区划分,每次至少分配一个区。区存储与段中,它由连续的数据块组成。
⑷、数据块
数据块是数据库中最小的数据组织单位与管理单位,是数据文件磁盘存储空间单位,也是数据库I/O的最小单位,数据块大小由DB_BLOCK_SIZE参数决定,不同的Oracle版本DB_BLOCK_SIZE的默认值是不同的。
⑸、模式对象
模式对象是一种应用,包括:表、聚簇、视图、索引序列生成器、同义词、哈希、程序单元、数据库链等。
最后,在来说一下Oracle的用户、表空间和数据文件的关系:
一个用户可以使用一个或多个表空间,一个表空间也可以供多个用户使用。用户和表空间没有隶属关系,表空间是一个用来管理数据存储的逻辑概念,表空间只是和数据文件发生关系,数据文件是物理的,一个表空间可以包含多个数据文件,而一个数据文件只能隶属一个表空间。
总结:解释数据库、表空间、数据文件、表、数据的最好办法就是想象一个装满东西的柜子。数据库其实就是柜子,柜中的抽屉是表空间,抽屉中的文件夹是数据文件,文件夹中的纸是表,写在纸上的信息就是数据。
2.两类表空间:
系统SYSTEM表空间 非系统表空间 NON-SYSTEM表空间
系统SYSTEM表空间与数据库一起建立,在系统表空间中有数据字典,系统还原段。可以存放用户数据但是不建议。
非系统表空间NON-SYSTEM表空间 由管理员创建。可以方便管理。
3.3 实例的整体架构
实例整体架构图:
┌──────────────────────────────┐
│ Instance │
│ ┌──────────────────────────┐ │
│ │ ┌────────┐ SGA │ │
│ │ │ Shared Pool │ │ │
│ │ │ ┌─────┐ │ ┌────┐ ┌────┐ │ │
│ │ │ │Library │ │ │Database│ │ Redo │ │ │
│ │ │ │ Cache │ │ │Buffer │ │ Log │ │ │
│ │ │ └─────┘ │ │Cache │ │ Buffer │ │ │
│ │ │ ┌─────┐ │ │ │ │ │ │ │
│ │ │ │Data Dict │ │ └────┘ └────┘ │ │
│ │ │ │ Cache │ │ │ │
│ │ │ └─────┘ │ │ │
│ │ └────────┘ │ │
│ └──────────────────────────┘ │
│ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌───┐│
│ │PMON│ │SNON│ │DBWR│ │LGWR│ │CHPT│ │OTHERS││
│ └──┘ └──┘ └──┘ └──┘ └──┘ └───┘│
└──────────────────────────────┘
实例由内存和后台进程组成,它暂时存在于RAM和CPU中。当关闭运行的实例时,实例将随即消失。数据库由磁盘上的物理文件组成,不管在运行状态还是停止状态,这些文件就一直存在。因此,实例的生命周期就是其在内存中存在的时间,可以启动和停止。一旦创建数据库,数据库将永久存在。通俗的讲数据库就相当于平时安装某个程序所生成的安装目录,而实例就是运行某个程序时所需要的进程及消耗的内存。
Oracle的内存架构包含两部分系统全局区(SGA)和程序全局区(PGA)。
3.3.1 程序全局区
3.3.2 系统全局区
在操作系统提供的共享内存段实现的内存结构称为系统全局区(SGA)。SGA在实例启动时分配,在关闭时释放。在一定范围内,可以在实例运行时通过自动方式或响应DBA的指令,重新调整11g实例中的SGA及其中的组件的大小。
由上图可知SGA至少包含三种数据结构:数据库缓冲区缓存、日志缓冲区及共享池。还可能包括:大池、JAVA池。可以使用show sga,查看sga的状态。
1、共享池
a.库缓存是内存区域,按其已分析的格式存储最近执行的代码。分析就是将编程人员编写的代码转换为可执行的代码,这是oracle根据需要执行的一个过程。通过将代码缓存在共享池,可以在不重新分析的情况下重用,极大地提高性能。
b.数据字典缓存有时称为“行缓存”,它存储最近使用的对象定义:表、索引、用户和其他元数据定义的描述。
c.PL/SQL区:存储的PL/SQL对象是过程、函数、打包的过程、打包的函数、对象类型定义和触发器。
2、数据库缓冲区
数据库缓冲区是oracle用来执行SQL的工作区域。
3、日志缓冲区
日志缓冲区是小型的、用于短期存储将写入到磁盘上的重做日志的变更向量的临时区域。日志缓冲区在启动实例时分配,如果不重新启动实例,就不能在随后调整其大小。
后台进程有:
1、PMON—–程序监控器
2、SMON—–系统监控区
3、DBWR—–数据写进程
4、LGWR—–日志写进程
5、CKPT—–检查点进程
6、Others—归档进程
四、SQL语法基础(DDL、DCL、TCL、DML)
SQL 全名是结构化查询语言(Structured Query Language),是用于数据库中的标准数据查询语言,IBM 公司最早使用在其开发的数据库系统中。1986 年 10 月,美国 ANSI 对 SQL 进行规范后,以此作为关系式数据库管理系统的标准语言 (ANSI X3. 135-1986),1987 年得到国际标准组织的支持下成为国际标准。不过各种通行的数据库系统在其实践过程中都对 SQL 规范作了某些编改和扩充。所以,实际上不同数据库系统之间的 SQL 语言不能完全相互通用
DML 语句(数据操作语言)Insert、Update、 Delete、Select
DDL 语句(数据定义语言)Create、Alter、 Drop
DCL 语句(数据控制语言)Grant、Revoke
TCL 事务控制语句 Commit 、Rollback、Savepoint
4.1 入门语句
普通用户连接: Conn scott/tiger
超级管理员连接: Conn “sys/bluesot as sysdba”
Disconnect; 断开连接————-disc
Save c:\1.txt 把 SQL 存到文件
Ed c:\1.txt 编辑 SQL 语句
@ c:\1.txt 运行 SQL 语句
Desc emp; 描述 Emp 结构
Select * from tab; 查看该用户下的所有对象
Show user; 显示当前用户
如果在 sys 用户下: 查询 Select * from emp; 会报错,原因:emp 是属于 scott,所以此时必须使用:select * from scott.emp; / 运行上一条语句
4.2 DDL(数据定义语言)—-改变表结构
4.2.1 创建表
CREATE TABLE name (
tid VARCHAR2(5),
tname VARCHAR2(20),
tdate DATE,
as VARCHAR(7,2)
);
4.2.2 添加一列
ALTER TABLE 表名 ADD 列名 属性;
ALTER TABLE student ADD age number(5);
4.2.4 删除一列
ALTER TABLE table_name DROP COLUMN column_name;
ALTER TABLE student DROP COLUMN age;
4.2.5 修改一列的属性
ALTER TABLE table_name MODIFY column_name type;
修改列名
ALTER TABLE table_name RENAME COLUMN columnname TO newname;
修改表名
ALTER TABLE table_name RENAME TO newname;
4.2.6 查看表中数据
DESC table_name;
4.2.7 删除表
DROP TABLE table_name;
4.3 DCL(数据控制语言)
4.3.1 授权
GRANT 权限/角色 TO 用户
给 demo 用户以创建 session 的权限:GRANT create session TO demo;
角色:————-角色就是一堆权限的集合Create role myrole;
Grant create table to myrole;
Drop role myrole; 删除角色
1.CONNECT, RESOURCE, DBA
这些预定义角色主要是为了向后兼容。其主要是用于数据库管理。oracle建议用户自己设计数据库管理和安全的权限规划,而不要简单的使用这些预定角色。将来的版本中这些角色可能不会作为预定义角色。
2.DELETE_CATALOG_ROLE, EXECUTE_CATALOG_ROLE, SELECT_CATALOG_ROLE 这些角色主要用于访问数据字典视图和包。
3.EXP_FULL_DATABASE, IMP_FULL_DATABASE 这两个角色用于数据导入导出工具的使用。
GRANT 权限(select、update、insert、delete) ON schema.table TO 用户
|- GRANT select ON scott.emp TO test ;
|- Grant all on scott.emp to test; –将表相关的所有权限付给 test
|- Grant update(ename) on emp to test; 可以控制到列(还有 insert)
4.3.2 收权
REVOKE 权限/角色 ON schema.table FROM 用户
|- REVOKE select ON scott.emp FROM test ;
4.4 TCL(事务控制语言)
事务的概念:事务是一系列对数据库操作命令的集合,它有边界(commit—commit)
事务的特征:A ——原子性—–不可分割性——-要么执行要么不执行
C——一致性—–rollback
I——-隔离性—–锁的机制来保证的,锁有粒度【表、行】———只读、修改锁
上锁—SELECT * FROM DEMO1 FOR UPDATE;
释放——commit、rollback
D —–持久性——-commit
系统时间—–sysdate
to_date(‘2013/11/09’,‘yyyy/mm/dd’)——–修改日期格式
转换函数
1、To_char
select to_char(sysdate,’yyyy’) from dual;
select to_char(sysdate,’fmyyyy-mm-dd’) from dual;
select to_char(sal,’L999,999,999’) from emp;
select to_char(sysdate,’D’) from dual;//返回星期
2、To_number
select to_number(‘13’)+to_number(‘14’) from dual;
3、To_date
Select to_date(‘20090210’,‘yyyyMMdd’) from dual;
4.4.1 ROLLBACK
回滚——回滚到它上面的离它最近的commit
4.4.2 COMMIT
提交———–将数据缓冲区的数据提交到文件中去
4.4.3 SAVEPOINT——保存点
回滚点例子:
INSERT INTO DEMO1(TID,TNAME) VALUES(11,’AS’);
SAVEPOINT P1;
INSERT INTO DEMO1(TID,TNAME) VALUES(22,’AS’);
INSERT INTO DEMO1(TID,TNAME) VALUES(33,’AS’);
SAVEPOINT P2;
INSERT INTO DEMO1(TID,TNAME) VALUES(44,’AS’);
ROLLBACK TO P2;
COMMIT;
INSERT INTO DEMO1(TID,TNAME) VALUES(55,’AS’);
ROLLBACK TO P1;———-无法回滚
————查询结果:11,22,33 由于55没有提交所以没有写入文件
4.5 DML(数据操作语言)———改变数据结构
4.5.1 insert 语句
INSERT INTO table_name() VALUES();
INSERT INTO table_name VALUES();
插入空值时,用三种格式
1、INSERT INTO demo VALUES(”);
2、INSERT INTO demo VALUES(’ ‘);
3、INSERT INTO demo VALUES(NULL);
INSERT INTO demo(tid,tname,tdate) VALUES(1,null,sysdate);
INSERT INTO demo(tid,tname,tdate) VALUES(1,”,to_date(sysdate,’yyyy-mm-dd’));
INSERT INTO demo VALUES(1,”,to_date(‘2013/11/11’,’yyyy/mm/dd’));
注意:
1、字符串类型的字段值必须用单引号括起来,如:‘rain’;
2、如果字段值里包含单引号需要进行字符串转换,把它替换成两个单引号‘’,如:‘”c”’ 数据库中将插入‘c’;
3、字符串类型的字段值超过定义长度会报错,最好在插入前进行长度校验;
4、日期字段的字段值可以使用当前数据库的系统时间sysdate,精确到秒;
5、INSERT时如果要用到从1开始自动增长的序列号,应该先建立一个序列号。
4.5.2 update 语句
UPDATE table_name SET column_name=值 WHERE 查询条件
UPDATE demo SET tname=’张三’ WHERE tid=1;
COMMIT;——是更新生效
4.5.3 delete 语句—–不能回退
DELETE TABLE table_name;————–只能删除数据,不能释放空间
TRUNCATE TABLE table_name;—————删除数据并释放空间
DELETE TABLE demo;
TRUNCATE TABLE demo;
4.5.4 select 语句
A 、简单的 Select 语句
SELECT * FROM table_name;
SELECT * FROM demo;
B、空值 is null
SELECT * FROM demo where tname is null;
第五章 Select查询
结构: 执行顺序
SELECT * 5
FROM table_name 1
WHERE —–分组前的条件 2
GROUP BY 3
HAVING —–分组后的条件 4
ORDER BY column_name ASC/DESC; 6 —————————– 子句、聚合函数、友好列 列名 AS 别名
5.1 简单查询
5.1.1 查看表结构
DESC table_name;
DESC emp;
5.1.2查看所有的列
SELECT * FROM table_name;
SELECT * FROM emp;
5.1.3 查看指定列
SELECT 列名 FROM table_name;
SELECT empno FROM emp;
5.1.4 查看指定行
SELECT * FROM table_name WHERE column_name=值;
SELECT * FROM emp WHERE empno=7369;
5.1.5 使用算术表达式 + 、- 、/ 、*
SELECT ename,sal+1 FROM EMP;
ENAME SAL+1
SMITH 801
ALLEN 1601
WARD 1251
SELECT ename,sal-2 FROM EMP;
ENAME SAL-1
SMITH 799
ALLEN 1599
WARD 1249
SELECT ename,sal*2 FROM EMP;
ENAME SAL*2
SMITH 1600
ALLEN 3200
WARD 2500
SELECT ename,sal/2 FROM EMP;
ENAME SAL/2
SMITH 400
ALLEN 800
WARD 625
nvl(comm,0)的意思是,如果comm中有值,则nvl(comm,0)=comm; comm中无值,则nvl(comm,0)=0
5.1.3 连接运算符 ||
SELECT ‘how’||’do’ ||sno;
5.1.4 使用字段别名 AS
SELECT * FROM DEMO1 “Asd”;——-带引号可以保证输入的结果有大小写
SELECT * FROM DEMO1 asd;
5.1.5 空值 IS NULL、 IS NOT NULL
SQL>SELECT * FROM EMP WHERE COMM IS NOT NULL AND SAL IN(SELECT ENAME,sal FROM emp WHERE ENAME LIKE ‘_O%’) ;
5.1.6 去除重复行 DISTINCT
SQL>SELECT DISTINCT DEPTNO FROM EMP ORDER BY DEPTNO
SQL>SELECT DEPTNO FROM EMP GROUP BY DEPTNO ORDER BY DEPTNO
5.1.7 查询结果排序 ORDER BY ASC/DESC
逆序:
SQL> SELECT * FROM emp WHERE job=’CLERK’ OR ename=’MILLER’ ORDER BY SAL DESC;
顺序:
SQL> SELECT * FROM emp WHERE job=’CLERK’ OR ename=’MILLER’ ORDER BY SAL ASC;
SQL> SELECT * FROM emp WHERE job=’CLERK’ OR ename=’MILLER’ ORDER BY SAL ;
5.1.8 关系运算符 >、 < 、=、(!= or <>) MOD(模,类似于%)、BETWEEN AND、 NOT BETWEEN AND
SQL> SELECT DISTINCT MGR FROM emp WHERE MGR<>7788;
SQL>SELECT DISTINCT MGR FROM emp WHERE MGR BETWEEN 7788 AND 7902;
SQL>SELECT * FROM emp WHERE MOD(DEPTNO,100)=2;
SQL>SELECT * FROM EMP1 WHERE EMPNO NOT BETWEEN 7369 AND 7521;
SQL>SELECT * FROM EMP1 WHERE EMPNO>=7369 AND EMPNO<=7521;
5.1.9 操作 IN、NOT IN
SQL> SELECT ENAME,SAL FROM EMP WHERE SAL IN(SELECT MAX(SAL) FROM EMP1 GROUP BY DEPTNO);
5.1.10 模糊查询 LIKE、NOT LIKE—-只针对字符型
% 表示零或多个字符
_ 表示一个字符
对于特殊符号可使用ESCAPE 标识符来查找
select * from emp where ename like ‘%_%’ escape ‘’
上面的escape表示*后面的那个符号不当成特殊字符处理,就是查找普通的_符号
5.1.11 逻辑运算符 AND、OR、NOT
SQL> select * from emp where job=’CLERK’ OR ename=’MILLER’;
SQL> select * from emp where job=’CLERK’ AND ename=’MILLER’;
5.1.12 ANY、ALL
SQL>SELECT * FROM EMP WHERE EMPNO IN(7369,7521,7782);
SQL>SELECT * FROM EMP WHERE EMPNO >ANY(7369,7521,7782);–大于min
SQL>SELECT * FROM EMP WHERE EMPNO >ALL(7369,7521,7782);–大于max
SQL>SELECT * FROM EMP WHERE EMPNO< ANY(7369,7521,7782);–小于max
SQL>SELECT * FROM EMP WHERE EMPNO< ALL(7369,7521,7782);–小于min
5.1.13 练习
1、选择在部门30中员工的所有信息
Select * from emp where deptno=30;
2、列出职位为(MANAGER)的员工的编号,姓名
Select empno,ename from emp where job =”Manager”;
3、找出奖金高于工资的员工
Select * from emp where comm>sal;
4、找出每个员工奖金和工资的总和
Select sal+comm,ename from emp;
5、找出部门10中的经理(MANAGER)和部门20中的普通员工(CLERK)
Select * from emp where (deptno=10 and job=?MANAGER?) or (deptno=20 and job=?CLERK?);
6、找出部门10中既不是经理也不是普通员工,而且工资大于等于2000的员工
Select * from emp where deptno=10 and job not in(‘MANAGER’) and sal>=2000;
7、找出有奖金的员工的不同工作
Select distinct job from emp where comm is not null and comm>0;
8、找出没有奖金或者奖金低于500的员工
Select * from emp where comm<500 or comm is null;
9、显示雇员姓名,根据其服务年限,将最老的雇员排在最前面
select ename from emp order by hiredate ;
10、SQL>SELECT DEPTNO,MAX(SAL) AS tot,
MIN(COMM) AS MCOMM,
SUM(COMM) SUMC,
TRUNC(AVG(SAL+NVL(COMM,0)))TAVG,
ROUND(AVG(SAL+NVL(COMM,0)),2)RAVG,
COUNT(*)
FROM EMP
HAVING COUNT(*) >3
GROUP BY DEPTNO
ORDER BY DEPTNO
11、SQL>SELECT ENAME,SAL
FROM EMP
WHERE SAL IN(SELECT MAX(SAL)
FROM EMP1
GROUP BY DEPTNO);
5.2 多表查询
INNER JOIN:内连接
JOIN: 如果表中有至少一个匹配,则返回行
LEFT JOIN: 即使右表中没有匹配,也从左表返回所有的行
RIGHT JOIN: 即使左表中没有匹配,也从右表返回所有的行
FULL JOIN: 只要其中一个表中存在匹配,就返回行
5.2.1、笛卡尔集(Cross Join)————列相加,行相乘
Select * from emp,dept;
5.2.2、等值连接(Equijoin)(Natural join..on)———隐式内联
select empno, ename, sal, emp.deptno, dname from emp, dept where emp.deptno = dept.deptno;
5.2.3、非等值连接(Non-Equijoin)
select ename,empno,grade from emp,salgrade where sal between losal and hisal;
5.2.4、自联接(Self join)
select column_name from table_name1,table_name2 where 条件;
select e.empno,e.ename,m.empno,m.ename from emp e,emp m where m.mgr = e.empno;
5.2.5、内联接(Inner Join)———-显式外联
SELECT column_name(s)
FROM table_name1
INNER JOIN table_name2
ON table_name1.column_name=table_name2.column_name
5.2.6、 左外联接(Left Outer Join )
左外连接:在内联接的基础上,增加左表中没有匹配的行,也就是说,左表中的记录总会出现在最终结果集中
SELECT column_name(s)
FROM table_name1
LEFT JOIN table_name2
ON table_name1.column_name=table_name2.column_name
select s.sid,s.sname,s1.sid,s1.sname from student s,student1 s1 where s.sid=s1.sid(+);
1、先通过from自句判断左表和右表;
2、接着看加号作用在那个表别名上;
3、如果作用在右表上,则为左外连接,否则为右外连接
select empno,ename,dname from emp left outer join dept on emp.deptno = dept.deptno;
5.2.7、右外联接(Right Outer Join)
右外连接:在内联接的基础上,增加右表中没有匹配的行,也就是说,右表中的记录总会出现在最终结果集中
SELECT column_name(s)
FROM table_name1
RIGHT JOIN table_name2
ON table_name1.column_name=table_name2.column_name
右外联接转换为内联接
SELECT column_name(s)
FROM table_name1
RIGHT JOIN table_name2
ON table_name1.column_name=table_name2.column_name WHERE table_name1.column_name is not null;
select s.sid,s.sname,s1.sid,s1.sname from student s,student1 s1 where s.sid(+)=s1.sid;
select empno,ename,dname from emp right outer join dept on emp.deptno= dept.deptno;
select * from emp1 e right join dept d on e.deptno=d.deptno where e.deptno is not null;
5.2.8、全外联接(Full Outer Join)
SELECT column_name(s)
FROM table_name1
FULL JOIN table_name2
ON table_name1.column_name=table_name2.column_name
select empno,ename,dname from emp full outer join dept on emp.deptno = dept.deptno;
5.2.9、集合操作
· UNION:并集,所有的内容都查询,重复的显示一次
·UNION ALL:并集,所有的内容都显示,包括重复的
· INTERSECT:交集:只显示重复的
· MINUS:差集:只显示对方没有的(跟顺序是有关系的)
SELECT column_name(s) FROM table_name1
UNION/INTERSECT/MINUS
SELECT column_name(s) FROM table_name2
首先建立一张只包含 20 部门员工信息的表:
CREATE TABLE emp20 AS SELECT * FROM emp WHERE deptno=20 ;
1、验证 UNION 及 UNION ALL
UNION:SELECT * FROM emp UNION SELECT * FROM emp20 ;
使用此语句重复的内容不再显示了
UNION ALL:SELECT * FROM emp UNION ALL SELECT * FROM emp20 ;
重复的内容依然显示
2、验证 INTERSECT
SELECT * FROM emp INTERSECT SELECT * FROM emp20 ; 只显示了两个表中彼此重复的记录。
MINUS:返回差异的记录
SELECT * FROM emp MINUS SELECT * FROM emp20 ; 只显示了两张表中的不同记录满链接也可以用以下的方式来表示:
select t1.id,t2.id from table1 t1,table t2 where t1.id=t2.id(+) union
select t1.id,t2.id from table1 t1,table t2 where t1.id(+)=t2.id
5.3 子查询
5.3.1、单行子查询
select * from emp
where sal > (select sal from emp where empno = 7566);
5.3.2、 子查询空值/多值问题
如果子查询未返回任何行,则主查询也不会返回任何结果
(空值)select * from emp where sal > (select sal from emp where empno = 8888);
如果子查询返回单行结果,则为单行子查询,可以在主查询中对其使用相应的单行记录比较运算符
(正常)select * from emp where sal > (select sal from emp where empno = 7566);
如果子查询返回多行结果,则为多行子查询,此时不允许对其使用单行记录比较运算符
(多值)select * from emp where sal > (select avg(sal) from emp group by deptno);//非法—-错误的
5.3.3、 多行子查询
select * from emp where sal > any(select avg(sal) from emp group by deptno);
select * from emp where sal > all(select avg(sal) from emp group by deptno);
select * from emp where job in (select job from emp where ename = ‘MARTIN’ or ename = ‘SMITH’);
5.3.4、 分页查询
Oracle分页
①采用rownum关键字(三层嵌套)
SELECT * FROM(
SELECT A.*,ROWNUM num FROM (
SELECT * FROM t_order)A
WHERE ROWNUM<=15)
WHERE num>=5; –返回第5-15行数据
②采用row_number解析函数进行分页(效率更高)
SELECT xx.* FROM(
SELECT t.*,row_number() over(ORDER BY o_id)AS num
FROM t_order t
)xx
WHERE num BETWEEN 5 AND 15;
ROWID和ROWNUM的区别
1、ROWID是物理地址,用于定位ORACLE中具体数据的物理存储位置是唯一的18位物理编号,而ROWNUM则是根据sql查询后得到的结果自动加上去的
2、ROWNUM是暂时的并且总是从1开始排起,而ROWID是永久的。
解析函数能用格式
函数() over(pertion by 字段 order by 字段);
ROW_NUMBER(): Row_number函数返回一个唯一的值,当碰到相同数据时,排名按照记录集中记录的顺序依次递增。 row_number()和rownum差不多,功能更强一点(可以在各个分组内从1开时排序),因为row_number()是分析函数而rownum是伪列所以row_number()一定要over而rownum不能over。
RANK():Rank函数返回一个唯一的值,除非遇到相同的数据,此时所有相同数据的排名是一样的,同时会在最后一条相同记录和下一条不同记录的排名之间空出排名。rank()是跳跃排序,有两个第二名时接下来就是第四名(同样是在各个分组内)。
DENSE_RANK():Dense_rank函数返回一个唯一的值,除非当碰到相同数据,此时所有相同数据的排名都是一样的。
dense_rank()是连续排序,有两个第二名时仍然跟着第三名。他和row_number的区别在于row_number是没有重复值的。
dense_rank()是连续排序,有两个第二名时仍然跟着第三名。他和row_number的区别在于row_number是没有重复值的。
下面举个例子:
SQL> select * from user_order order by customer_sales;
REGION_ID CUSTOMER_ID CUSTOMER_SALES
10 30 1216858
5 2 1224992
9 24 1224992
9 23 1224992
8 18 1253840
【3】row_number()、rank()、dense_rank()这三个分析函数的区别实例
SQL>SELECT empno, deptno, SUM(sal) total,
RANK() OVER(ORDER BY SUM(sal) DESC) RANK,
dense_rank() OVER(ORDER BY SUM(sal) DESC)dense_rank,
row_number() OVER(ORDER BY SUM(sal) DESC)row_number
FROM emp1 GROUP BY empno, deptno;
1 7839 10 5000 1 1 1
2 7902 20 3000 2 2 2
3 7788 20 3000 2 2 3
4 7566 20 2975 4 3 4
5 7698 30 2850 5 4 5
6 7782 10 2450 6 5 6
7 7499 30 1600 7 6 7
比较上面3种不同的策略,我们在选择的时候就要根据客户的需求来定夺了:
①假如客户就只需要指定数目的记录,那么采用row_number是最简单的,但有漏掉的记录的危险
②假如客户需要所有达到排名水平的记录,那么采用rank或dense_rank是不错的选择。至于选择哪一种则看客户的需要,选择dense_rank或得到最大的记录
Mysql分页采用limt关键字
select * from t_order limit 5,10; #返回第6-15行数据
select * from t_order limit 5; #返回前5行
select * from t_order limit 0,5; #返回前5行
Mssql 2000分页采用top关键字(20005以上版本也支持关键字rownum)
Select top 10 * from t_order where id not in (select id from t_order where id>5 ); //返回第6到15行数据其中10表示取10记录 5表示从第5条记录开始取
sql server 分页采用top关键字
5.3.5、 in 、exists
EXISTS 的执行流程
select * from t1 where exists ( select null from t2 where y = x )
可以理解为:
for x in ( select * from t1 ) loop
if ( exists ( select null from t2 where y = x.x ) then
OUTPUT THE RECORD
end if;
end loop;
对于 in 和 exists 的性能区别:
如果子查询得出的结果集记录较少,主查询中的表较大且又有索引时应该用 in,反之如果外层的主查询记录较少,子查询中的表大,又有索引时使用 exists。
其实我们区分 in 和 exists 主要是造成了驱动顺序的改变(这是性能变化的关键),如果是 exists,那么以外层表为驱动表,先被访问,如果是 IN,那么先执行子查询,所以我们会以驱动表的快速返回为目标,那么就会考虑到索引及结果集的关系了 另外 IN 是不对 NULL 进行处理
如:
select 1 from dual where not in (0,1,2,null) 为空
第六章 约束
约束就是指对插入数据的各种限制,例如:人员的姓名不能为空,人的年龄只能在0~150 岁之间。约束可以对数据库中的数据进行保护。 约束可以在建表的时候直接声明,也可以为已建好的表添加约束。
6.1、NOT NULL:非空约束 例如:姓名不能为空
CREATE TABLE person(
pid NUMBER ,
name VARCHAR2(30) NOT NULL
) ;
alter table emp
– 插入数据
INSERT INTO person(pid,name) VALUES (11,’张三’);
– 错误的数据,会受到约束限制,无法插入
INSERT INTO person(pid) VALUES (12);
6.2、 PRIMARY KEY:主键约束
· 不能重复,不能为空 · 例如:身份证号不能为空。 现在假设pid字段不能为空,且不能重复。
DROP TABLE person ;
CREATE TABLE person
(
pid NUMBER PRIMARY KEY , name VARCHAR(30) NOT NULL
) ;
– 插入数据
INSERT INTO person(pid,name) VALUES (11,’张三’);
– 主键重复了
INSERT INTO person(pid,name) VALUES (11,’李四’);
6.3、UNIQUE:唯一约束,值不能重复(空值除外) 人员中有电话号码,电话号码不能重复。
DROP TABLE person ;
CREATE TABLE person
(
pid NUMBER PRIMARY KEY NOT NULL , name VARCHAR(30) NOT NULL , tel VARCHAR(50) UNIQUE
) ;
– 插入数据
INSERT INTO person(pid,name,tel) VALUES (11,’张三’,’1234567’);
– 电话重复了
INSERT INTO person(pid,name,tel) VALUES (12,’李四’,’1234567’);
6.4、CHECK:条件约束,插入的数据必须满足某些条件
例如:人员有年龄,年龄的取值只能是 0~150 岁之间
DROP TABLE person ;
CREATE TABLE person(
pid NUMBER PRIMARY KEY NOT NULL ,
name VARCHAR2(30) NOT NULL ,
tel VARCHAR2(50) NOT NULL UNIQUE ,
age NUMBER CHECK(age BETWEEN 0 AND 150)
) ;
– 插入数据
INSERT INTO person(pid,name,tel,age) VALUES (11,’张三’,’1234567’,30);
– 年龄的输入错误
INSERT INTO person(pid,name,tel,age) VALUES (12,’李四’,’2345678’,-100);
alter table product
add constriant chk_product_unitprice check(unitprice>0);
6.5、Foreign Key:外键
例如:有以下一种情况:
· 一个人有很多本书:
|- Person 表
|- Book 表:而且book 中的每一条记录表示一本书的信息,一本书的信息属于一个人
CREATE TABLE book(
bid NUMBER PRIMARY KEY NOT NULL ,
name VARCHAR(50) ,
– 书应该属于一个人 pid NUMBER
) ;
如果使用了以上的表直接创建,则插入下面的记录有效:
INSERT INTO book(bid,name,pid) VALUES(1001,’JAVA’,12) ;
以上的代码没有任何错误,但是没有任何意义,因为一本书应该属于一个人,所以在此处的pid的取值应该与person 表中的pid一致。
此时就需要外键的支持。修改book 的表结构
DROP TABLE book ;
CREATE TABLE book
(
bid NUMBER PRIMARY KEY NOT NULL , name VARCHAR(50) ,
– 书应该属于一个人 pid NUMBER REFERENCES person(pid) ON DELETE CASCADE
– 建立约束:book_pid_fk,与person中的pid 为主-外键关系
–CONSTRAINT book_pid_fk FOREIGN KEY(pid) REFERENCES person(pid)
) ;
INSERT INTO book(bid,name,pid) VALUES(1001,’JAVA’,12) ;
6.6、级联删除
此时如果想完成删除person 表的数据同时自动删除掉book 表的数据操作,则必须使用级联删除。
在建立外键的时候必须指定级联删除(ON DELETE CASCADE)。
CREATE TABLE book
(
bid NUMBER PRIMARY KEY NOT NULL , name VARCHAR(50) ,
– 书应该属于一个人
pid NUMBER ,
– 建立约束:book_pid_fk,与person中的pid 为主-外键关系
CONSTRAINT book_pid_fk FOREIGN KEY(pid) REFERENCES person(pid) ON DELETE CASCADE
) ;
DROP TABLE book ;
DROP TABLE person ;
CREATE TABLE person(
pid NUMBER ,
name VARCHAR(30) NOT NULL ,
tel VARCHAR(50) ,
age NUMBER
) ;
CREATE TABLE book(
bid NUMBER ,
name VARCHAR2(50) ,
pid NUMBER
) ;
以上两张表中没有任何约束,下面使用 alter 命令为表添加约束
ALTER TABLE table_name ADD CONSTRAINT column_name 约束;
1、 为两个表添加主键:
· person 表 pid 为主键:
ALTER TABLE person ADD CONSTRAINT person_pid_pk PRIMARY KEY(pid) ;
· book 表 bid 为主键:
ALTER TABLE book ADD CONSTRAINT book_bid_pk PRIMARY KEY(bid) ;
2、 为 person 表中的 tel 添加唯一约束:
ALTER TABLE person ADD CONSTRAINT person_tel_uk UNIQUE(tel) ;
3、 为 person 表中的 age 添加检查约束:
ALTER TABLE person ADD CONSTRAINT person_age_ck CHECK(age BETWEEN 0 AND
150) ;
4、 为 book 表中的 pid 添加与 person 的主-外键约束,要求带级联删除
ALTER TABLE book ADD CONSTRAINT person_book_pid_fk FOREIGN KEY (pid)
REFERENCES person(pid) ON DELETE CASCADE ;
Q:如何用alter添加非空约束
A:用 check约束
6.7、删除约束:
ALTER TABLE book DROP CONSTRAINT person_book_pid_fk ;
alter table student drop unique(tel);
6.8、 启用约束
ALTER TABLE book enable CONSTRAINT person_book_pid_fk ;
6.9、 禁用约束
ALTER TABLE book disable CONSTRAINT person_book_pid_fk ;
第七章 SQL函数
7.1 单行函数
1、字符函数
Upper ——-大写
SELECT Upper (‘abcde’) FROM dual ;
SELECT * FROM emp WHERE ename=UPPER(‘smith’) ;
Lower ——–小写
SELECT lower(‘ABCDE’) FROM dual ;
Initcap——–首字母大写
Select initcap(ename) from emp;
Concat—-联接
Select concat(‘a’,’b’) from dual;
Select ‘a’||’b’ from dual;
Substr——截取
Select substr(‘abcde’,length(‘abcde’)-2) from dual;
Select substr(‘abcde’,-3,3) from dual;
Length——长度
Select length(ename) from emp;
Replace——替代
Select replace(ename,’a’,’A’) from emp;
Instr———- indexof
Select instr(‘Hello World’,’or’) from dual;
Lpad————左侧填充 lpad() *****Smith
lpad(‘Smith’,10,’*’)
Rpad————-右侧填充 rpad()Smith*****
rpad(‘Smith’,10,’*’)
Trim————-过滤首尾空格 trim() Mr Smith
trim(’ Mr Smith ‘)
2、数值函数
Round
select round(412,-2) from dual; –400
select round(412.313,2) from dual;
Mod
select MOD(412,3) from dual;
Trunc
select trunc(412.13,-2) from dual;
3、日期函数
Months_between()
select months_between(sysdate,hiredate) from emp;
Add_months()
select add_months(sysdate,1) from dual;
Next_day()
select next_day(sysdate,’星期一’) from dual;
Last_day
select last_day(sysdate) from dual;
4.4、转换函数
To_char
select to_char(sysdate,’yyyy’) from dual; select to_char(sysdate,’fmyyyy-mm-dd’) from dual; select to_char(sal,’L999,999,999’) from emp; select to_char(sysdate,’D’) from dual;//返回星期
To_number
select to_number(‘13’)+to_number(‘14’) from dual;
To_date
Select to_date(?20090210?,?yyyyMMdd?) from dual;
5、通用函数
NVL()函数
select nvl(comm,0) from emp;
NULLIF()函数
如果表达式 exp1 与 exp2 的值相等则返回 null,否则返回 exp1 的值
NVL2()函数 select empno, ename, sal, comm, nvl2(comm, sal+comm, sal) total from emp;
COALESCE()函数
依次考察各参数表达式,遇到非 null 值即停止并返回该值。
select empno, ename, sal, comm, coalesce(sal+comm, sal, 0)总收入 from emp;
CASE 表达式——区间
SQL>select empno,
ename,
sal,
case deptno
when 10 then ‘财务部’
when 20 then ‘研发部’
when 30 then ‘销售部’
else ‘未知部门’
end 部门
from emp;
SQL>SELECT e.*,
CASE
WHEN sal>=3000 THEN ‘高’
WHEN sal>=2000 AND sal<3000 THEN ‘中’
WHEN sal<2000 AND sal>0 THEN ‘高’
ELSE ‘un’
END ca
FROM emp1 e;
DECODE()函数————离散的值
SQL>select empno, ename, sal,
decode(deptno,
10, ‘财务部’,
20, ‘研发部’,
30, ‘销售部’,
‘未知部门’) 部门
from emp;
单行函数嵌套
select empno, lpad(initcap(trim(ename)),10,’ ‘) name, job, sal from emp;
7.2 分组函数
1、COUNT
如果数据库表的没有数据,count(*)返回的不是 null,而是 0
2、Avg,max,min,sum
3、nvl—-分组函数与空值
分组函数省略列中的空值
select avg(comm) from emp; select sum(comm) from emp;
可使用 NVL()函数强制分组函数处理空值 select avg(nvl(comm, 0)) from emp;
4、GROUP BY 子句
出现在 SELECT 列表中的字段或者出现在 order by 后面的字段,如果不是包含在分组函数中,那么该字段必须同时在 GROUP BY 子句中出现。包含在 GROUP BY 子句中的字段则不必须出现在 SELECT 列表中。
可使用 where 字句限定查询条件,可使用 Order by 子句指定排序方式
如果没有 GROUP BY 子句,SELECT 列表中不允许出现字段(单行函数)与分组函数混用的情况。
select empno, sal from emp; //合法 select avg(sal) from emp; //合法 select empno, initcap(ename), avg(sal) from emp; //非法
不允许在 WHERE 子句中使用分组函数。 select deptno, avg(sal) from emp where avg(sal) > 2000; group by deptno;
5、HAVING 子句
select deptno, job, avg(sal)
from emp
where hiredate >= to_date(‘1981-05-01’,’yyyy-mm-dd’) group by deptno,job having avg(sal) > 1200 order by deptno,job;
6、分组函数嵌套
select max(avg(sal)) from emp group by deptno;
第八章 PLSQL变量与常量
1、声明变量
变量一般都在PL/SQL块的声明部分声明,引用变量前必须首先声明,要在执行或异常处理部分使用变量,那么变量必须首先在声明部分进行声明。
声明变量的语法如下:
Variable_name [CONSTANT] databyte [NOT NULL][:=|DEFAULT expression]
注意:可以在声明变量的同时给变量强制性的加上NOT NULL约束条件,此时变量在初始化时必须赋值。
2、给变量赋值
给变量赋值有两种方式:
. 直接给变量赋值
X:=200;
Y=Y+(X*20);
. 通过SQL SELECT INTO 或FETCH INTO给变量赋值
SELECT SUM(SALARY),SUM(SALARY*0.1)
INTO TOTAL_SALARY,TATAL_COMMISSION
FROM EMPLOYEE
WHERE DEPT=10;
3、常量
常量与变量相似,但常量的值在程序内部不能改变,常量的值在定义时赋予,,他的声明方式与变量相似,但必须包括关键字CONSTANT。常量和变量都可被定义为SQL和用户定义的数据类型。
ZERO_VALUE CONSTANT NUMBER:=0;
4、标量(scalar)数据类型
标量(scalar)数据类型没有内部组件,他们大致可分为以下四类:
. number
. char
. date/time
. boolean
表1 Numer
Datatype Range Subtypes description
BINARY_INTEGER -214748-2147483647 NATURAL
NATURAL
NPOSITIVE
POSITIVEN
SIGNTYPE
用于存储单字节整数。
要求存储长度低于NUMBER值。
用于限制范围的子类型(SUBTYPE):
NATURAL:用于非负数
POSITIVE:只用于正数
NATURALN:只用于非负数和非NULL值
POSITIVEN:只用于正数,不能用于NULL值
SIGNTYPE:只有值:-1、0或1.
NUMBER 1.0E-130-9.99E125 DEC
DECIMAL
DOUBLE
PRECISION
FLOAT
INTEGERIC
INT
NUMERIC
REAL
SMALLINT 存储数字值,包括整数和浮点数。可以选择精度和刻度方式,语法:
number[([,])]。
缺省的精度是38,scale是0.
PLS_INTEGER -2147483647-2147483647 与BINARY_INTEGER基本相同,但采用机器运算时,PLS_INTEGER提供更好的性能 。
表2 字符数据类型
datatype rang subtype description
CHAR 最大长度32767字节 CHARACTER 存储定长字符串,如果长度没有确定,缺省是1
LONG 最大长度2147483647字节 存储可变长度字符串
RAW 最大长度32767字节 用于存储二进制数据和字节字符串,当在两个数据库之间进行传递时,RAW数据不在字符集之间进行转换。
LONGRAW 最大长度2147483647 与LONG数据类型相似,同样他也不能在字符集之间进行转换。
ROWID 18个字节 与数据库ROWID伪列类型相同,能够存储一个行标示符,可以将行标示符看作数据库中每一行的唯一键值。
VARCHAR2 最大长度32767字节 STRINGVARCHAR 与VARCHAR数据类型相似,存储可变长度的字符串。声明方法与VARCHAR相同
表3 DATE和BOOLEAN
datatype range description
BOOLEAN TRUE/FALSE 存储逻辑值TRUE或FALSE,无参数
DATE 01/01/4712 BC 存储固定长的日期和时间值,日期值中包含时间
LOB数据类型
LOB(大对象,Large object) 数据类型用于存储类似图像,声音这样的大型数据对象,LOB数据对象可以是二进制数据也可以是字符数据,其最大长度不超过4G。LOB数据类型支持任意访问方式,LONG只支持顺序访问方式。LOB存储在一个单独的位置上,同时一个”LOB定位符”(LOB locator)存储在原始的表中,该定位符是一个指向实际数据的指针。在PL/SQL中操作LOB数据对象使用ORACLE提供的包DBMS_LOB.LOB数据类型可分为以下四类:
. BFILE—————-二进制文件
. BLOB————–二进制对象
. CLOB—————字符型对象
. NCLOB————-nchar类型对象
操作符
PL/SQL有一系列操作符。操作符分为下面几类:
. 算术操作符
. 关系操作符
. 比较操作符
. 逻辑操作符
算术操作符如表4所示
operator operation
+ 加
- 减
/ 除
* 乘
** 乘方
关系操作符主要用于条件判断语句或用于where子串中,关系操作符检查条件和结果是否为true或false,
表5PL/SQL中的关系操作符
operator operation
< 小于操作符
<= 小于或等于操作符
大于操作符
= 大于或等于操作符
= 等于操作符
!= 不等于操作符
<> 不等于操作符
:= 赋值操作符
表6 显示的是比较操作符
operator operation
IS NULL 如果操作数为NULL返回TRUE
LIKE 比较字符串值
BETWEEN 验证值是否在范围之内
IN 验证操作数在设定的一系列值中
表7显示的是逻辑操作符
operator operation
AND 两个条件都必须满足
OR 只要满足两个条件中的一个
NOT 取反
第九章 PL/SQL——–动态SQL
PL/SQL动态SQL(原创)
概念:动态SQL,编译阶段无法明确SQL命令,只有在运行阶段才能被识别
动态SQL和静态SQL两者的异同
静态SQL为直接嵌入到PL/SQL中的代码,而动态SQL在运行时,根据不同的情况产生不同的SQL语句。
静态SQL在执行前编译,一次编译,多次运行。动态SQL同样在执行前编译,但每次执行需要重新编译。
静态SQL可以使用相同的执行计划,对于确定的任务而言,静态SQL更具有高效性,但缺乏灵活性;动态SQL使用了不同的执行计划,效率不如静态SQL,但能够解决复杂的问题。
动态SQL容易产生SQL注入,为数据库安全带来隐患。
动态SQL语句的几种方法
a.使用EXECUTE IMMEDIATE语句
包括DDL语句,DCL语句,DML语句以及单行的SELECT 语句。该方法不能用于处理多行查询语句。
b.使用OPEN-FOR,FETCH和CLOSE语句
对于处理动态多行的查询操作,可以使用OPEN-FOR语句打开游标,使用FETCH语句循环提取数据,最终使用CLOSE语句关闭游标。
c.使用批量动态SQL
即在动态SQL中使用BULK子句,或使用游标变量时在fetch中使用BULK ,或在FORALL语句中使用BULK子句来实现。
d.使用系统提供的PL/SQL包DBMS_SQL来实现动态SQL
动态SQL的语法
下面是动态SQL常用的语法之一
EXECUTE IMMEDIATE dynamic_SQL_string
[INTO defined_variable1, defined_variable2, …]
[USING [IN | OUT | IN OUT] bind_argument1, bind_argument2,…]
[{RETURNING | RETURN} field1, field2, … INTO bind_argument1,
bind_argument2, …]
语法描述
dynamic_SQL_string:存放指定的SQL语句或PL/SQL块的字符串变量
defined_variable1:用于存放单行查询结果,使用时必须使用INTO关键字,类似于使用
SELECT ename INTO v_name FROM scott.emp;
只不过在动态SQL时,将INTO defined_variable1移出到dynamic_SQL_string语句之外。
bind_argument1:用于给动态SQL语句传入或传出参数,使用时必须使用USING关键字,IN表示传入的参数,OUT表示传出的参数,IN OUT则既可以传入,也可传出。
RETURNING | RETURN 子句也是存放SQL动态返回值的变量。
使用要点
a.EXECUTE IMMEDIATE执行DML时,不会提交该DML事务,需要使用显示提交(COMMIT)或作为EXECUTE IMMEDIATE自身的一部分。
b.EXECUTE IMMEDIATE执行DDL,DCL时会自动提交其执行的事务。
c.对于多行结果集的查询,需要使用游标变量或批量动态SQL,或者使用临时表来实现。
d.当执行SQL时,其尾部不需要使用分号,当执行PL/SQL 代码时,其尾部需要使用分号。
f.动态SQL中的占位符以冒号开头,紧跟任意字母或数字表示。
动态SQL的使用(DDL,DCL,DML以及单行结果集)
DECLARE
v_table VARCHAR2(30):=’emp1’;
v_sql LONG:=’SELECT ename FROM emp1 WHERE empno=:1’;–占位符\可以用任何符号(数字或字母)
–v_no emp1.empno%TYPE:=’&请输入号码’;–&是变量绑定符 绑定的数据是字符串类型时必须要加引号
–v_no emp1.empno%TYPE:=&请输入号码;
v_name emp1.ename%TYPE;
BEGIN
–EXECUTE IMMEDIATE v_sql INTO v_name USING IN v_no;
dbms_output.put_line(‘ename=’||v_name);
–EXECUTE IMMEDIATE ‘create table emp2 as select * from emp’;
–EXECUTE IMMEDIATE ‘drop table ‘||v_table;
–EXECUTE IMMEDIATE ‘alter table emp enable row movement’;
END;
BULK子句和动态SQL的使用
动态SQL中使用BULK子句的语法
EXECUTE IMMEDIATE dynamic_string –dynamic_string用于存放动态SQL字符串
[BULK COLLECT INTO define_variable[,define_variable…]] –存放查询结果的集合变量
[USING bind_argument[,argument…]] –使用参数传递给动态SQL
[{RETURNING | RETURN} –返回子句
BULK COLLECT INTO return_variable[,return_variable…]]; –存放返回结果的集合变量
使用bulk collect into子句处理动态SQL中T的多行查询可以加快处理速度,从而提高应用程序的性能。当使用bulk子句时,集合类型可以是PL/SQL所支持的索引表、嵌套表和VARRY,但集合元 素必须使用SQL数据类型。常用的三种语句支持BULK子句,分别为EXECUTE IMMEDIATE,FETCH 和FORALL。
使用EXECUTE IMMEDIATE 结合BULK子句处理多行查询。使用了BULK COLLECT INTO来传递结果。
SQL>declare
type ename_table_type is table of emp1.ename%type index by binary_integer;
type sal_table_type is table OF emp1.sal%type index by binary_integer;
ename_table ename_table_type;
sal_table sal_table_type;
sql_stat varchar2(100);
v_dno number := 7369;
begin
sql_stat := ‘select ename,sal from emp1 where deptno = :v_dno’; –动态DQL语句,未使用RETURNING子句
execute immediate sql_stat
bulk collect into ename_table,sal_table using v_dno;
for i in 1..ename_table.count loop
dbms_output.put_line(‘Employee ’ || ename_table(i) || ’ Salary is: ’ || sal_table(i));
end loop;
end;
使用FETCH子句结合BULK子句处理多行结果集
下面的示例中首先定义了游标类型,游标变量以及复合类型,复合变量,接下来从动态SQL中OPEN游标,然后使用FETCH将结果存放到复合变量中。即使用OPEN,FETCH代替了EXECUTE IMMEDIATE来完成动态SQL的执行。
SQL> declare
2 type empcurtype is ref cursor;
3 emp_cv empcurtype;
4 type ename_table_type is table of tb2.ename%type index by binary_integer;
5 ename_table ename_table_type;
6 sql_stat varchar2(120);
7 begin
8 sql_stat := ‘select ename from tb2 where deptno = :dno’;
9 open emp_cv for sql_stat using &dno;
10 fetch emp_cv bulk collect into ename_table;
11 for i in 1..ename_table.count loop
12 dbms_output.put_line(‘Employee Name: ’ || ename_table(i));
13 end loop;
14 close emp_cv;
15* end;
在FORALL语句中使用BULK子句
下面是FORALL子句的语法
FORALL index IN lower bound..upper bound –FORALL循环计数
EXECUTE IMMEDIATE dynamic_string –结合EXECUTE IMMEDIATE来执行动态SQL语句
USING bind_argument | bind_argument(index) –绑定输入参数
[bind_argument | bind_argument(index)]…
[{RETURNING | RETURN} BULK COLLECT INTO bind_argument[,bind_argument…]]; –绑定返回结果集
FORALL子句允许为动态SQL输入变量,但FORALL子句仅支持的DML(INSERT,DELETE,UPDATE)语句,不支持动态的SELECT语句。
下面的示例中,首先声明了两个复合类型以及复合变量,接下来为复合变量ename_table赋值,以形成动态SQL语句。紧接着使用FORALL子句结合EXECUTE IMMEDIATE 来提取结果集。
SQL> declare
2 type ename_table_type is table of tb2.ename%type;
3 type sal_table_type is table of tb2.sal%type;
4 ename_table ename_table_type;
5 sal_table sal_table_type;
6 sql_stat varchar2(100);
7 begin
8 ename_table := ename_table_type(‘BLAKE’,’FORD’,’MILLER’);
9 sql_stat := ‘update tb2 set sal = sal * 1.1 where ename = :1’
10 || ’ returning sal into :2’;
11 forall i in 1..ename_table.count
12 execute immediate sql_stat using ename_table(i) returning bulk collect into sal_table;
13 for j in 1..sal_table.count loop
14 dbms_output.put_line(‘The ’ || ename_table(j) || ”” || ‘s new salalry is ’ || sal_table(j));
15 end loop;
16* end;
常见错误
1、使用动态DDL时,不能使用绑定变量。
EXECUTE IMMEDIATE ‘CREATE TABLE dsa ‘||’as select * from :1’ USING IN v_table;
解决办法,将绑定变量直接拼接,如下:
EXECUTE IMMEDIATE ‘CREATE TABLE dsa ‘||’as select * from ‘|| v_table;
2、不能使用schema对象作为绑定参数,下面的示例中,动态SQL语句查询需要传递表名,因此收到了错误提示。
EXECUTE IMMEDIATE ‘SELECT COUNT(*) FROM:tb_name ’ into v_count;
解决办法,将绑定变量直接拼接,如下:
EXECUTE IMMEDIATE ‘SELECT COUNT(*) FROM ’ || tb_name into v_count;
3、动态SQL块不能使用分号结束(;)
execute immediate ‘select count(*) from emp;’ –此处多出了分号,应该去掉
4、动态PL/SQL块不能使用正斜杠来结束块,但是块结尾处必须要使用分号(;)
SQL> declare
2 plsql_block varchar2(300);
3 begin
4 plsql_block := ‘Declare ’ ||
5 ’ v_date date; ’ ||
6 ’ begin ’ ||
7 ’ select sysdate into v_date from dual; ’ ||
8 ’ dbms_output.put_line(to_char(v_date,”yyyy-mm-dd”)); ’ ||
9 ’ end;
10 /’; –此处多出了/,应该将其去掉
11 execute immediate plsql_block;
12* end;
4、空值传递的问题
下面的示例中对表tb_emp更新,并将空值更新到sal列,直接使用USING NULL收到错误提示。
execute immediate sql_stat using null,v_empno;
正确的处理方法
v_sal tb2.sal%type; –声明一个新变量,但不赋值
execute immediate sql_stat using v_sal,v_empno;
5、日期和字符型必须要使用引号来处理
DECLARE
sql_stat VARCHAR2(100);
v_date DATE :=TO_DATE(‘2013-11-21’,’yyyy-mm-dd’);
v_empno NUMBER :=7900;
v_ename emp.ename%TYPE;
v_sal emp.sal%TYPE;
BEGIN
sql_stat := ‘SELECT ename,sal FROM emp WHERE hiredate=:v_date’;
EXECUTE IMMEDIATE sql_stat
INTO v_ename,v_sal
USING v_date;
DBMS_OUTPUT.PUT_LINE(‘Employee Name ‘||v_ename||’, sal is ‘||v_sal);
END;
6、单行SELECT 查询不能使用RETURNING INTO返回
下面的示例中,使用了动态的单行SELECT查询,并且使用了RETURNING子句来返回值。事实上,RETURNING coloumn_name INTO 子句仅仅支持对DML结果集的返回,因此,收到了错误提示。
SQL>declare
v_empno emp.empno%type := &empno;
v_ename emp.ename%type;
begin
execute immediate ‘select ename from emp where empno = :eno’ into v_ename using v_empno;
dbms_output.put_line(‘Employee name: ’ || v_ename);
end;
第十章 PL/SQL———游标
游标的使用
在 PL/SQL 程序中,对于处理多行记录的事务经常使用游标来实现。
4.1 游标概念
对于不同的SQL语句,游标的使用情况不同:
SQL语句
游标
非查询语句
隐式的
结果是单行的查询语句
隐式的或显示的
结果是多行的查询语句
显示的
游标名%属性名
显式游标的名字右用户定义,隐式游标名为SQL
隐式游标只是一个状态
4.1.1 处理显式游标
显式游标处理
语法格式:
CURSOR cursor_name is select * from emp;
OPEN cursor_name;
FETCH cursor_name INTO variables_list;
CLOSE cursor_name;
例1:
DECLARE
v_deptno NUMBER:=&inputno;
v_row emp1%ROWTYPE;
CURSOR v_cursor IS SELECT * FROM emp1 WHERE deptno=v_deptno;
BEGIN
–打开
OPEN v_cursor;
–提取
LOOP
FETCH v_cursor INTO v_row;–先提取
EXIT WHEN v_cursor%NOTFOUND;
dbms_output.put_line(‘Employee Name: ’ || v_row.ename || ’ ,Salary: ’ || v_row.sal);
END LOOP;
–关闭
CLOSE v_cursor;
END;
显式游标处理需四个 PL/SQL步骤:
l 定义/声明游标:就是定义一个游标名,以及与其相对应的SELECT 语句。
格式:
CURSOR cursor_name[(parameter[, parameter]…)]
[RETURN datatype]
IS
select_statement;
select_statement;
游标参数只能为输入参数,其格式为:
parameter_name [IN] datatype [{:= | DEFAULT} expression]
在指定数据类型时,不能使用长度约束。如NUMBER(4),CHAR(10) 等都是错误的。
[RETURN datatype]是可选的,表示游标返回数据的数据。如果选择,则应该严格与select_statement中的选择列表在次序和数据类型上匹配。一般是记录数据类型或带“%ROWTYPE”的数据。
l 打开游标:就是执行游标所对应的SELECT 语句,将其查询结果放入工作区,并且指针指向工作区的首部,标识游标结果集合。如果游标查询语句中带有FOR UPDATE选项,OPEN 语句还将锁定数据库表中游标结果集合对应的数据行。
格式:
OPEN cursor_name[([parameter =>] value[, [parameter =>] value]…)];
在向游标传递参数时,可以使用与函数参数相同的传值方法,即位置表示法和名称表示法。PL/SQL 程序不能用OPEN 语句重复打开一个游标。
l 提取游标数据:就是检索结果集合中的数据行,放入指定的输出变量中。
格式:
FETCH cursor_name INTO {variable_list | record_variable };
执行FETCH语句时,每次返回一个数据行,然后自动将游标移动指向下一个数据行。当检索到最后一行数据时,如果再次执行FETCH语句,将操作失败,并将游标属性%NOTFOUND置为TRUE。所以每次执行完FETCH语句后,检查游标属性%NOTFOUND就可以判断FETCH语句是否执行成功并返回一个数据行,以便确定是否给对应的变量赋了值。
l 对该记录进行处理;
l 继续处理,直到活动集合中没有记录;
l 关闭游标:当提取和处理完游标结果集合数据后,应及时关闭游标,以释放该游标所占用的系统资源,并使该游标的工作区变成无效,不能再使用FETCH 语句取其中数据。关闭后的游标可以使用OPEN 语句重新打开。
格式:
CLOSE cursor_name;
注:定义的游标不能有INTO 子句。
例1. 查询前10名员工的信息。
DECLARE
CURSOR c_cursor
IS SELECT first_name || last_name, Salary FROM EMPLOYEES WHERE rownum<11;
v_ename EMPLOYEES.first_name%TYPE;
v_sal EMPLOYEES.Salary%TYPE;
BEGIN
OPEN c_cursor;
FETCH c_cursor INTO v_ename, v_sal;
WHILE c_cursor%FOUND LOOP
DBMS_OUTPUT.PUT_LINE(v_ename||’—’||to_char(v_sal) );
FETCH c_cursor INTO v_ename, v_sal;
END LOOP;
CLOSE c_cursor;
END;
2.游标属性
Cursor_name%FOUND 布尔型属性,当最近一次提取游标操作FETCH成功则为 TRUE,否则为FALSE;
Cursor_name%NOTFOUND 布尔型属性,与%FOUND相反;
Cursor_name%ISOPEN 布尔型属性,当游标已打开时返回 TRUE;
Cursor_name%ROWCOUNT 数字型属性,返回已从游标中读取的记录数。
例2:没有参数且没有返回值的游标。
DECLARE
v_f_name employees.first_name%TYPE;
v_j_id employees.job_id%TYPE;
CURSOR c1 –声明游标,没有参数没有返回值
IS
SELECT first_name, job_id FROM employees WHERE department_id = 20;
BEGIN
OPEN c1; –打开游标
LOOP
FETCH c1 INTO v_f_name, v_j_id; –提取游标
IF c1%FOUND THEN
DBMS_OUTPUT.PUT_LINE(v_f_name||’的岗位是’||v_j_id);
ELSE
DBMS_OUTPUT.PUT_LINE(‘已经处理完结果集了’);
EXIT;
END IF;
END LOOP;
CLOSE c1; –关闭游标
END;
例3:有参数且没有返回值的游标。
DECLARE
v_f_name employees.first_name%TYPE;
v_h_date employees.hire_date%TYPE;
CURSOR c2(dept_id NUMBER, j_id VARCHAR2) –声明游标,有参数没有返回值
IS
SELECT first_name, hire_date FROM employees WHERE department_id = dept_id AND job_id = j_id;
BEGIN
OPEN c2(90, ‘AD_VP’); –打开游标,传递参数值
LOOP
FETCH c2 INTO v_f_name, v_h_date; –提取游标
IF c2%FOUND THEN
DBMS_OUTPUT.PUT_LINE(v_f_name||’的雇佣日期是’||v_h_date);
ELSE
DBMS_OUTPUT.PUT_LINE(‘已经处理完结果集了’);
EXIT;
END IF;
END LOOP;
CLOSE c2; –关闭游标
END;
例4:有参数且有返回值的游标。
DECLARE
TYPE emp_record_type IS RECORD(
f_name employees.first_name%TYPE,
h_date employees.hire_date%TYPE);
v_emp_record EMP_RECORD_TYPE;
v_emp_record EMP_RECORD_TYPE;
CURSOR c3(dept_id NUMBER, j_id VARCHAR2) –声明游标,有参数有返回值
RETURN EMP_RECORD_TYPE
IS
SELECT first_name, hire_date FROM employees WHERE department_id = dept_id AND job_id = j_id;
BEGIN
OPEN c3(j_id => ‘AD_VP’, dept_id => 90); –打开游标,传递参数值
LOOP
FETCH c3 INTO v_emp_record; –提取游标
IF c3%FOUND THEN
DBMS_OUTPUT.PUT_LINE(v_emp_record.f_name||’的雇佣日期是’||v_emp_record.h_date);
ELSE
DBMS_OUTPUT.PUT_LINE(‘已经处理完结果集了’);
EXIT;
END IF;
END LOOP;
CLOSE c3; –关闭游标
END;
例5:基于游标定义记录变量。
DECLARE
CURSOR c4(dept_id NUMBER, j_id VARCHAR2) –声明游标,有参数没有返回值
IS
SELECT first_name f_name, hire_date FROM employees WHERE department_id = dept_id AND job_id = j_id;
–基于游标定义记录变量,比声明记录类型变量要方便,不容易出错
v_emp_record c4%ROWTYPE;
BEGIN
OPEN c4(90, ‘AD_VP’); –打开游标,传递参数值
LOOP
FETCH c4 INTO v_emp_record; –提取游标
IF c4%FOUND THEN
DBMS_OUTPUT.PUT_LINE(v_emp_record.f_name||’的雇佣日期是’||v_emp_record.hire_date);
ELSE
DBMS_OUTPUT.PUT_LINE(‘已经处理完结果集了’);
EXIT;
END IF;
END LOOP;
CLOSE c4; –关闭游标
END;
3. 游标的FOR循环
PL/SQL语言提供了游标FOR循环语句,自动执行游标的OPEN、FETCH、CLOSE语句和循环语句的功能;当进入循环时,游标FOR循环语句自动打开游标,并提取第一行游标数据,当程序处理完当前所提取的数据而进入下一次循环时,游标FOR循环语句自动提取下一行数据供程序处理,当提取完结果集合中的所有数据行后结束循环,并自动关闭游标。
格式:
FOR index_variable IN cursor_name[(value[, value]…)] LOOP
– 游标数据处理代码
END LOOP;
其中:
index_variable为游标FOR 循环语句隐含声明的索引变量,该变量为记录变量,其结构与游标查询语句返回的结构集合的结构相同。在程序中可以通过引用该索引记录变量元素来读取所提取的游标数据,index_variable中各元素的名称与游标查询语句选择列表中所制定的列名相同。如果在游标查询语句的选择列表中存在计算列,则必须为这些计算列指定别名后才能通过游标FOR 循环语句中的索引变量来访问这些列数据。
注:不要在程序中对游标进行人工操作;不要在程序中定义用于控制FOR循环的记录。
例6:当所声明的游标带有参数时,通过游标FOR 循环语句为游标传递参数。
DECLARE
CURSOR c_cursor(dept_no NUMBER DEFAULT 10)
IS
SELECT department_name, location_id FROM departments WHERE department_id <= dept_no;
BEGIN
DBMS_OUTPUT.PUT_LINE(‘当dept_no参数值为30:’);
FOR c1_rec IN c_cursor(30) LOOP
DBMS_OUTPUT.PUT_LINE(c1_rec.department_name||’—’||c1_rec.location_id);
END LOOP;
DBMS_OUTPUT.PUT_LINE(CHR(10)||’使用默认的dept_no参数值10:’);
FOR c1_rec IN c_cursor LOOP
DBMS_OUTPUT.PUT_LINE(c1_rec.department_name||’—’||c1_rec.location_id);
END LOOP;
END;
例7:PL/SQL还允许在游标FOR循环语句中使用子查询来实现游标的功能。
BEGIN
–隐含打开游标
FOR r IN (SELECT * FROM emp1 WHERE deptno=v_deptno) LOOP
–隐含执行一个FETCH语句
dbms_output.put_line(‘Employee Name: ’ || r.ename || ’ ,Salary: ’ || r.sal);
–隐含监测c_sal%NOTFOUND
END LOOP;
–隐含关闭游标
END;
4.1.2 处理隐式游标
对于非查询语句,如修改、删除操作,则由ORACLE 系统自动地为这些操作设置游标并创建其工作区,这些由系统隐含创建的游标称为隐式游标,隐式游标的名字为SQL,这是由ORACLE 系统定义的。对于隐式游标的操作,如定义、打开、取值及关闭操作,都由ORACLE 系统自动地完成,无需用户进行处理。用户只能通过隐式游标的相关属性,来完成相应的操作。在隐式游标的工作区中,所存放的数据是与用户自定义的显示游标无关的、最新处理的一条SQL 语句所包含的数据。
格式调用为: SQL%
注:INSERT, UPDATE, DELETE, SELECT(单查询) 语句中不必明确定义游标。
隐式游标属性
属性
值
SELECT
INSERT
UPDATE
DELETE
SQL%ISOPEN
FALSE
FALSE
FALSE
FALSE
SQL%FOUND
TRUE
有结果
成功
成功
SQL%FOUND
FALSE
没结果
失败
失败
SQL%NOTFUOND
TRUE
没结果
失败
失败
SQL%NOTFOUND
FALSE
有结果
成功
失败
SQL%ROWCOUNT
返回行数,只为1
插入的行数
修改的行数
删除的行数
例8: 通过隐式游标SQL的%ROWCOUNT属性来了解修改了多少行。
DECLARE
v_rows NUMBER;
BEGIN
–更新数据
UPDATE employees SET salary = 30000
WHERE department_id = 90 AND job_id = ‘AD_VP’;
–获取默认游标的属性值
v_rows := SQL%ROWCOUNT;
DBMS_OUTPUT.PUT_LINE(
DBMS_OUTPUT.PUT_LINE(‘更新了’||v_rows||’个雇员的工资’);
–回退更新,以便使数据库的数据保持原样
ROLLBACK;
END;
4.1.3 使用游标更新和删除数据
游标修改和删除操作是指在游标定位下,修改或删除表中指定的数据行。这时,要求游标查询语句中必须使用FOR UPDATE选项,以便在打开游标时锁定游标结果集合在表中对应数据行的所有列和部分列。
为了对正在处理(查询)的行不被另外的用户改动,ORACLE 提供一个 FOR UPDATE 子句来对所选择的行进行锁住。该需求迫使ORACLE锁定游标结果集合的行,可以防止其他事务处理更新或删除相同的行,直到您的事务处理提交或回退为止。
语法:
SELECT column_list FROM table_list FOR UPDATE [OF column[, column]…] [NOWAIT]
如果使用 FOR UPDATE 声明游标,则可在DELETE和UPDATE 语句中使用
WHERE CURRENT OF cursor_name子句,修改或删除游标结果集合当前行对应的数据库表中的数据行。
例9:从EMPLOYEES表中查询某部门的员工情况,将其工资最低定为 1500;
DECLARE
V_deptno employees.department_id
V_deptno employees.department_id%TYPE :=&p_deptno;
CURSOR emp_cursor
IS
SELECT employees.employee_id, employees.salary FROM employees WHERE employees.department_id=v_deptno FOR UPDATE NOWAIT;
BEGIN
FOR emp_record IN emp_cursor LOOP
IF emp_record.salary < 1500 THEN
UPDATE employees SET salary=1500 WHERE CURRENT OF emp_cursor;
END IF;
END LOOP;
COMMIT;
END;
4.2 游标变量
与游标一样,游标变量也是一个指向多行查询结果集合中当前数据行的指针。但与游标不同的是,游标变量是动态的,而游标是静态的。游标只能与指定的查询相连,即固定指向一个查询的内存处理区域,而游标变量则可与不同的查询语句相连,它可以指向不同查询语句的内存处理区域(但不能同时指向多个内存处理区域,在某一时刻只能与一个查询语句相连),只要这些查询语句的返回类型兼容即可。
4.2.1 声明游标变量
游标变量为一个指针,它属于参照类型,所以在声明游标变量类型之前必须先定义游标变量类型。在PL/SQL中,可以在块、子程序和包的声明区域内定义游标变量类型。
语法格式为:
1.定义游标变量
TYPE cursortype IS REF CURSOR;
cursor_variable cursortype;
2.打开游标变量
OPEN cursor_variable FOR dynamic_string
[USING bind_argument[,bind_argument]…]
3.循环提取数据
FETCH cursor_variable INTO {var1[,var2]…| record_variable};
EXIT WHEN cursor_variable%NOTFOUND
4.关闭游标变量
CLOSE cursor_variable;
例10:使用游标变量(没有RETURN子句)
DECLARE
–定义一个游标数据类型
TYPE emp_cursor_type IS REF CURSOR;
–声明一个游标变量
c1 EMP_CURSOR_TYPE;
–声明两个记录变量
v_emp_record employees%ROWTYPE;
v_reg_record regions%ROWTYPE;
BEGIN
OPEN c1 FOR SELECT * FROM employees WHERE department_id = 20;
LOOP
FETCH c1 INTO v_emp_record;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_emp_record.first_name||’的雇佣日期是’||v_emp_record.hire_date);
END LOOP;
–将同一个游标变量对应到另一个SELECT语句
OPEN c1 FOR SELECT * FROM regions WHERE region_id IN(1,2);
LOOP
FETCH c1 INTO v_reg_record;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_reg_record.region_id||’表示’||v_reg_record.region_name);
END LOOP;
CLOSE c1;
END;
例11:使用游标变量(有RETURN子句)
DECLARE
–定义一个与employees表中的这几个列相同的记录数据类型
TYPE emp_record_type IS RECORD(
f_name employees.first_name
f_name employees.first_name%TYPE,
h_date employees.hire_date
h_date employees.hire_date%TYPE,
j_id employees.job_id
j_id employees.job_id%TYPE);
–声明一个该记录数据类型的记录变量
v_emp_record EMP_RECORD_TYPE;
–定义一个游标数据类型
TYPE emp_cursor_type IS REF CURSOR
RETURN EMP_RECORD_TYPE;
–声明一个游标变量
c1 EMP_CURSOR_TYPE;
BEGIN
OPEN c1 FOR SELECT first_name, hire_date, job_id FROM employees WHERE department_id = 20;
LOOP
FETCH c1 INTO v_emp_record;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(‘雇员名称:’||v_emp_record.f_name||’ 雇佣日期:’||v_emp_record.h_date||’ 岗位:’||v_emp_record.j_id);
END LOOP;
CLOSE c1;
END;