Oracle Database,又名Oracle RDBMS,简称Oracle。直至2022年仍是当今使用最广泛最流行的关系型数据库,由甲骨文公司开发、运营与维护。在数据库领域中一直处于领先地位,系统可移植性好、使用方便、功能与性能强大、效率高,适用于大、中、小软件开发。放眼当下因高效率(适应高吞吐量)、可靠性好仍是开发商业软件最好的选择(就是有点贵)。
这里我们主要学习Oracle
数据的相关知识为主,在本地安装Oracle
数据库安装过程时间非常长而且文件资源巨大(卸载更麻烦)!为了方便我们直接使用Docker
搭建oracle数据库环境即可。
# 1.oracle镜像拉取
docker pull registry.cn-hangzhou.aliyuncs.com/helowin/oracle_11g
# 2.运行容器
docker run -d -p 1521:1521 --name oracle registry.cn-hangzhou.aliyuncs.com/helowin/oracle_11g
# 3.查看是否启动成功
docker ps
容器启动成功后,我们只完成了最简单的一步,oracle与其他数据库不同,还需要额外添加一些配置才能使用客户端工具进行连接与访问。
# 1.进入容器
docker exec -it [containerId] /bin/bash
# 2.切换root用户(root用户密码是 helowin )
su root
# 3.配置oracle环境变量,注意容器没有安装vim/vi,也没有安装 apt-get 工具,因此这里我们选择以拷贝的方式修改profile文件
# 3.1/退出容器
exit
# 3.2/将容器中的profile文件拷贝出来
docker cp [containerId]:/etc/profile /home/oracle/profile
# 3.3/编辑profile文件,添加如下环境变量配置
vim /home/oracle/profile
export ORACLE_HOME=/home/oracle/app/oracle/product/11.2.0/dbhome_2
export ORACLE_SID=helowin
export PATH=$ORACLE_HOME/bin:$PATH
# 3.4/将profile文件拷贝会容器并重启
docker cp /home/oracle/profile [containerId]:/etc/profile
docker restart [containerId]
# 4.创建软连接(将某个文件在另一个位置建立一个同步的连接),注意进入容器后在root账号下执行
ln -s $ORACLE_HOME/bin/sqlplus /usr/bin
# 5.切换回oracle用户(必须要 - 连接,否则软连接无效)例如
[root@9fa414625962 /]# su - oracle
[oracle@9fa414625962 ~]$
# 6.登录sqlplus并修改sys和system用户密码
[oracle@9fa414625962 ~]$ sqlplus /nolog
SQL*Plus: Release 11.2.0.1.0 Production on Sat Mar 12 09:23:40 2022
Copyright (c) 1982, 2009, Oracle. All rights reserved.
SQL> # 这里面书写修改密码SQL语句
SQL> conn /as sysdba
Connected.
SQL> ALTER USER SYSTEM IDENTIFIED BY SYSTEM; # 修改system用户账号密码为 SYSTEM 客户端可用 SYSTEM/SYSTEM 进行登录
User altered.
SQL> ALTER USER SYS IDENTIFIED BY SYSTEM; # 修改sys用户账号密码为 SYSTEM
User altered.
SQL> CREATE USER LN IDENTIFIED BY LN; # 创建内部管理员账户密码
User created.
SQL> GRANT CONNECT,RESOURCE,DBA TO LN; # 将dba权限等授权给内部管理员账号和密码
Grant succeeded.
SQL> ALTER PROFILE DEFAULT LIMIT PASSWORD_LIFE_TIME UNLIMITED; # 修改密码规则策略为密码永不过期
Profile altered.
SQL> ALTER SYSTEM SET PROCESSES=1000 SCOPE=SPFILE; # 修改数据库最大连接
System altered.
# 7.修改以上信息后,需要重新启动数据库(依次执行一下命令即可)
conn /as sysdba
shutdown immediate;
startup;
# 8.记住这个命令 lsnrctl status 我们可以随时查看 oracle 的 Service instance
[oracle@9fa414625962 ~]$ lsnrctl status
至此oracle
数据库环境已经全部搭建完毕,我们就可以通过客户端工具进行访问与连接了,例如
另外需要注意的是客户端连接工具(Navicat Premium / PLSQL Developer等)访问Oracle
的时候需要配置Oracle
的客户端实例也称为OCI
环境。
我们是使用Docker
搭建的Oracle数据库环境,数据都是保存在容器中。假如我们不小心删除了容器,那么以前保存的数据库数据也会删除!因此启动Oracle
容器时也应该考虑数据持久化问题(使用数据卷技术)。
要想进行目录挂载实现数据双向同步,首先要知道在Linux中Oracle的数据库文件存放在哪里?这里可以使用SQL进行查询例如:
SELECT F.* FROM V$DBFILE F ORDER BY F.FILE# ASC;
docker run -d -p 1521:1521 -v /home/oracle/data:/home/oracle/app/oracle/oradata \
--name oracle registry.cn-hangzhou.aliyuncs.com/helowin/oracle_11g
ORACLE
数据库系统是美国ORACLE公司(甲骨文)提供的以分布式数据库为核心的一组软件产品,是目前最流行的客户 / 服务器(client / server 或 B / S)体系结构的数据库之一。迄今为止ORACLE
数据库是世界上使用最广泛的数据库管理系统。
ORACLE
数据库具有以下特点:
ORACLE
数据库相关概念:
1、数据库
oracle
数据库是数据的物理存储包括数据文件ORA或者DBF、控制文件、联机日志、参数文件等。其实ORACLE
数据库的概念与其他数据库不一样,这里的数据库是一个操作系统只有一个库,可以看作是ORACLE
就只有一个大数据库。
2、实例
一个ORACLE
实例(Oracle Instance)有一系列的后台进程(Background Processes)和内存结构(Memory Structures)组成,一个数据库可以有 n 个实例。例如前面我们搭建环境所连接的helowin
就是一个实例。
3、数据文件(dbf)
数据文件时数据库的物理存储单位,数据库的数据是存储在表空间的,真正是在某一个或者多个数据文件中。而一个表空间可以由一个或者多个数据文件组成,一个数据文件只能属于一个表空间。一旦数据文件被加入到某个表空间后,就不能删除这个文件,如果要删除某个数据文件,只能删除其所属于的表空间才行。
4、表空间(类似文件夹)
表空间是ORACLE
对物理数据库上相关数据文件(ORA或者DBF文件)的逻辑映射,一个数据库在逻辑上被划分成一到若干个表空间,每个表空间包含了在逻辑上相关联的一组结构。每个数据库至少有一个表空间(称之为SYSTEM表空间)。例如查看当前用户使用的表空间情况:
SELECT U.USERNAME, U.DEFAULT_TABLESPACE FROM USER_USERS U;
每个表空间由同一磁盘上的一个或多个文件组成,这些文件叫数据文件(datafile)一个数据文件只能属于一个表空间。
需要注意的是ORACLE
是由用户和表空间对数据进行管理和存放的,但是表不是由表空间去查询的,而是由用户去查的,因为不同用户可以在同一个表空间建立同一个名字的表!这里区分就是用户了。
5、用户
用户是在表空间下建立的,用户登录后只能看到和操作自己的表,ORACLE
的用户与MYSQL
的数据库类似,每建立一个应用需要创建一个用户。
可以这么说在MYSQL
中一个用户有多个数据库,而在ORACLE
中一个数据库有多个用户,恰好相反。
关于ORACLE
的相关概念就先说这边多,后面还会详细展开与接触。
学习环境的搭建我们还有一步未完成!向表中插入一些测试数据难免会使用中文,为避免出现乱码,还需要配置NLS_LANG
环境变量。
-- 检查 NLS_LANG 环境变量
SELECT USERENV('LANGUAGE') FROM DUAL;
--查询结果为 SIMPLIFIED CHINESE_CHINA.AL32UTF8
--配置上 NLS_LANG=SIMPLIFIED CHINESE_CHINA.AL32UTF8 即可如下图
认识ORACLE
首先从认识表空间开始:
-- 1.查看当前用户使用的表空间情况
SELECT U.* FROM USER_USERS U;
-- 2.查看所有表空间信息(名称、物理文件、大小等)
SELECT
TABLESPACE_NAME,
FILE_ID,
FILE_NAME,
ROUND(BYTES / ( 1024 * 1024 ), 0) TOTAL_SPACE
FROM
DBA_DATA_FILES
ORDER BY FILE_ID ASC;
-- 2.查看表空间使用情况
SELECT
SUM(BYTES) / (1024 * 1024) AS FREE_SPACE,
TABLESPACE_NAME
FROM
DBA_FREE_SPACE
GROUP BY
TABLESPACE_NAME;
-- 3.查看表空间使用情况(详细信息)
SELECT
A.TABLESPACE_NAME "表空间名",
TOTAL "表空间大小",
FREE "表空间剩余大小",
(TOTAL - FREE) "表空间使用大小",
TOTAL / (1024 * 1024 * 1024) "表空间大小(G)",
FREE / (1024 * 1024 * 1024) "表空间剩余大小(G)",
(TOTAL - FREE) / (1024 * 1024 * 1024) "表空间使用大小(G)",
ROUND( (TOTAL - FREE) / TOTAL, 4) * 100 "使用率 %"
FROM
(SELECT TABLESPACE_NAME, SUM(BYTES) FREE FROM DBA_FREE_SPACE GROUP BY TABLESPACE_NAME) A,
(SELECT TABLESPACE_NAME, SUM(BYTES) TOTAL FROM DBA_DATA_FILES GROUP BY TABLESPACE_NAME) B
WHERE 1 = 1
AND A.TABLESPACE_NAME = B.TABLESPACE_NAME
例如查看所有表空间信息(可自行测试下查询SQL)
那么如何创建自己的表空间呢?
-- 创建表空间 LN_TEST
CREATE TABLESPACE LN_TEST
DATAFILE '/home/oracle/app/oracle/oradata/helowin/ln_test.dbf'
SIZE 100M
AUTOEXTEND ON
NEXT 10M
再次查询所有表空间信息
表空间创建好后,如何给用户使用呢?也可以使用SQL语句进行指定
-- 1.给用户重新指定表空间(因为LN这个用户我们之前就创建好了)
ALTER USER LN DEFAULT TABLESPACE LN_TEST;
-- 2.再次检查当前用户使用的表空间
SELECT U.* FROM USER_USERS U;
-- 3.为了方便我们一般都是先创建表空间,然后再创建用户,创建用户时指定表空间。例如
CREATE USER [USER_NAME] IDENTIFIED BY [USER_PASSWORD] DEFAULT TABLESPACE [TABLESPACE_NAME];
-- 然后再给用户赋相应的权限即可 DBA 权限在 ORACLE 中权限是比较高的
GRANT DBA TO [USER_NAME];
建表语句其实绝大多数数据都遵循统一的规范,所以语法都是一样的,例如
-- Create table
create table DL_PROJECT_RELATION
(
discriminator NVARCHAR2(31) not null,
id NUMBER(32) not null,
fk_orgbusno NVARCHAR2(255) not null,
eaprojectid NUMBER(32),
parentprojectid NUMBER(32),
fk_project_id NUMBER(32),
remark NVARCHAR2(255),
type VARCHAR2(255),
businessid NUMBER(32)
)
tablespace GJ_INST
pctfree 10
initrans 1
maxtrans 255
storage
(
initial 64K
next 1M
minextents 1
maxextents unlimited
);
-- Add comments to the columns
comment on column DL_PROJECT_RELATION.id
is '主键';
comment on column DL_PROJECT_RELATION.fk_orgbusno
is '电力业务流水号';
comment on column DL_PROJECT_RELATION.eaprojectid
is '外网pid';
comment on column DL_PROJECT_RELATION.parentprojectid
is '主流程pid';
comment on column DL_PROJECT_RELATION.fk_project_id
is 'pid';
comment on column DL_PROJECT_RELATION.remark
is '关联标记';
comment on column DL_PROJECT_RELATION.type
is '类型';
comment on column DL_PROJECT_RELATION.businessid
is '业务id';
-- Create/Recreate indexes
create index DL_PROJECT_EA_PROJECT_ID on DL_PROJECT_RELATION (EAPROJECTID)
tablespace GJ_INST
pctfree 10
initrans 2
maxtrans 255
storage
(
initial 64K
next 1M
minextents 1
maxextents unlimited
);
create index DL_PROJECT_FK_ORGBUSNO on DL_PROJECT_RELATION (FK_ORGBUSNO)
tablespace GJ_INST
pctfree 10
initrans 2
maxtrans 255
storage
(
initial 64K
next 1M
minextents 1
maxextents unlimited
);
create index DL_PROJECT_FK_PROJECT_ID on DL_PROJECT_RELATION (FK_PROJECT_ID)
tablespace GJ_INST
pctfree 10
initrans 2
maxtrans 255
storage
(
initial 64K
next 1M
minextents 1
maxextents unlimited
);
create index DL_PROJECT_PP_PROJECT_ID on DL_PROJECT_RELATION (PARENTPROJECTID)
tablespace GJ_INST
pctfree 10
initrans 2
maxtrans 255
storage
(
initial 64K
next 1M
minextents 1
maxextents unlimited
);
-- Create/Recreate primary, unique and foreign key constraints
alter table DL_PROJECT_RELATION
add primary key (ID)
using index
tablespace GJ_INST
pctfree 10
initrans 2
maxtrans 255
storage
(
initial 64K
next 1M
minextents 1
maxextents unlimited
);
追加字段
ALTER TABLE LN.LN_USER ADD (
REMARK VARCHAR2(100)
)
修改字段类型
-- 仅限于没有数据的时候
ALTER TABLE LN.LN_USER MODIFY(REMARK NVARCHAR2(100) DEFAULT 'ALEX');
修改字段名
-- 语法如下
ALTER TABLE 表名称 RENAME COLUMN 原列名 TO 新列名;
-- 示例
ALTER TABLE LN.LN_USER RENAME COLUMN REMARK TO TYPE;
删除字段名
-- 语法如下
ALTER TABLE 表名称 DROP COLUMN 字段名1, 字段名2;
-- 示例
ALTER TABLE LN.LN_USER DROP COLUMN TYPE;
删除表
DROP TABLE [TABLE_NAME];
-- TRUNCATE DDL语句 会释放所有空间与数据包括索引等
TRUNCATE TABLE [TABLE_NAME];
以上语法大家了解即可包括插入更新等语句,工作中也很少用到,不会的时候查一下文档即可。重要的是要学会编写存储过程、索引、触发器、函数、游标、理解事务、分析执行计划进行索引优化等这才是工作中最常用的。
ORACLE
的数据类型也叫内置数据类型,可以按类型分为字符型、数字型、日期类型、LOB类型等实际开发中这四种类型是最常用的。
1、字符型
类型 | 描述 |
---|---|
CHAR 类型 |
定长字符串,会用空格填充来达到最大长度。例如非NULL的CHAR(12)总是包含12字节信息。CHAR类型字段最多可以存储2000字节信息,如果创建表是,不指定长度则默认为1。 |
VARCHAR2 类型 |
可变长字符串,最大可以存储4000字节的消息。不会使用空格填充至最大长度。 |
NVARCHAR2 类型 |
这是一个包含UNICODE格式数据的变长字符串。也就是说存储单位是字符(例如NVARCHAR2(10) 可以存10个汉字也也只可以存10个英文,而VARCHAR2(10) 可以存5个汉字,英文则可以存10个),最大可以存储4000字节的消息。 |
2、数字型 | |
类型 | 描述 |
- | - |
NUMBER 类型 |
NUMBER(P, S)是最常见的数字类型,P 是Precision 的英文缩写,即精度缩写,表示有效数字的位数,最多不能超过38 个有效数字。S 是Scale 的英文缩写,表示小数点数字的位数。例如 NUMBER(5) 最大可以存储的数为99999。NUMBER(5, 2) 最大可以存储的数为999.99. |
INTEGER 类型 |
INTEGER 是NUMBER 的子类型,它等同于NUMBER(38,0),用来存储整数。若插入、更新的数值有小数,则会被四舍五入。 |
3、日期类型 | |
类型 | 描述 |
- | - |
DATE 类型 |
DATE 是最常用的数据类型,日期数据类型存储日期和时间信息。虽然可以用字符或数字类型表示日期和时间信息,但是日期数据类型具有特殊关联的属性。为每个日期值,Oracle存储以下信息: 世纪、年、月、日期、小时、分钟和秒。一般占用7 个字节的存储空间。 |
TIMESTAMP 类型 |
这是一个7 字节或12 字节的定宽日期/时间数据类型。它与DATE 数据类型不同,因TIMESTAMP 可以包含小数秒,带小数秒的TIMESTAMP 在小数点右边最多可以保留9 位。 |
4、LOB类型(LOB类型分为BLOB和CLOB两种) | |
类型 | 描述 |
- | - |
BLOB 类型 |
BLOB即二进制大型对象(Binary Large Object),适用于存贮非文本的字节流数据(如程序、图象、影音等),最多能容纳4GB的数据。 |
CLOB 类型 |
CLOB,即字符型大型对象(Character Large Object),则与字符集相关,适于存贮文本型的数据(如历史档案、大部头著作等),最多能容纳4GB的数据。 |
ORACLE 实际上数据类型不止这些,这里只是列举了常用的数据类型。 |
由于和其他数据库大同小异,这里就简单介绍下
# 建表SQL
CREATE TABLE "LN"."LN_USER" (
"ID" NUMBER(10,0) NOT NULL,
"USERNAME" VARCHAR2(255 BYTE),
"PASSWORD" VARCHAR2(255 BYTE),
CONSTRAINT "SYS_C0011150" PRIMARY KEY ("ID"),
CONSTRAINT "SYS_C0011149" CHECK ("ID" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE
)
TABLESPACE "LN_TEST"
LOGGING
NOCOMPRESS
PCTFREE 10
INITRANS 1
STORAGE (
BUFFER_POOL DEFAULT
)
PARALLEL 1
NOCACHE
DISABLE ROW MOVEMENT
# 序列创建SQL(ORACLE里没有主键自增,主键大多使用序列)
CREATE SEQUENCE "LN"."S_LN_USER" MINVALUE 1 MAXVALUE 99999 INCREMENT BY 1 CACHE 20;
SELECT LN.S_LN_USER.NEXTVAL FROM DUAL;
插入语句
INSERT INTO LN.LN_USER(ID, USERNAME, PASSWORD) VALUES(LN.S_LN_USER.NEXTVAL, '马超', '123');
COMMIT;
SELECT U.* FROM LN.LN_USER U;
更新语句
UPDATE LN.LN_USER U SET U.USERNAME = '元歌' WHERE U.ID = 3;
COMMIT;
SELECT U.* FROM LN.LN_USER U;
删除语句
DELETE FROM LN.LN_USER U WHERE U.ID = 3;
COMMIT;
SELECT U.* FROM LN.LN_USER U;
# 还有一种删除方法区别于 DELETE 语句就是 TRUNCATE DDL 语句
# DELETE 删除的数据可以 ROLLBACK,删除可能产生碎片,并且不会释放空间
# TRUNCATE 直接摧毁表结构,释放所有资源,再重构表结构
学会使用程序访问ORACLE
数据库,也非常重要,最终我们都是以开发业务功能为主。
1、使用SpringBoot
的初始化向导创建一个测试工程
2、引入相关依赖(JDBC/数据库驱动)
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
<dependency>
<groupId>com.oracle.database.jdbcgroupId>
<artifactId>ojdbc6artifactId>
<version>11.2.0.4version>
dependency>
3、编写application.properties/application.yml
文件
server.port=8080
spring.application.name=oracle-test
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@121.41.571.179:1521:helowin
spring.datasource.username=LN
spring.datasource.password=LN
spring.jdbc.template.query-timeout=3
4、编写测试方法
@SpringBootTest
class DemoApplicationTests {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void oracleTest() {
String sql = "SELECT U.* FROM LN.LN_USER U";
List<Map<String, Object>> data = jdbcTemplate.queryForList(sql);
System.out.println(data);
}
}
使用SpringData JPA
访问ORACLE
数据库比较重要也是比较推荐的一种方式,这里我们要完成基本的增删改查功能。
1、使用SpringBoot
的初始化向导创建一个测试工程
2、引入相关依赖(JPA/数据库驱动)
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
<dependency>
<groupId>com.oracle.database.jdbcgroupId>
<artifactId>ojdbc6artifactId>
<version>11.2.0.4version>
dependency>
3、编写application.properties/application.yml
文件
server.port=8080
spring.application.name=oracle-test
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@121.41.571.179:1521:helowin
spring.datasource.username=LN
spring.datasource.password=LN
spring.jpa.show-sql=true
4、编写实体类(注意注解的使用方式)
/**
* @description: UserEntity
* @date: 2022/3/13 11:00
*/
@Entity
@Table(name = "LN_USER", schema = "LN") // schema属性在ORACLE数据库中填用户ID
@JsonIgnoreProperties(value = {"hibernateLazyInitializer", "handler"})
public class UserEntity implements Serializable {
private static final long serialVersionUID = 7521045049709813121L;
private Integer id;
private String username;
private String password;
@Id
@Column(name = "ID", unique = true, nullable = false)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "LN_USER_ID")
@SequenceGenerator(name = "LN_USER_ID", schema = "LN", sequenceName = "S_LN_USER", allocationSize = 1)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(name = "USERNAME")
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Column(name = "PASSWORD")
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
5、编写Mapper
接口,需要继承JPA
提供的JpaRepository
接口
/**
* @description: UserMapper
* @date: 2022/3/13 11:38
*/
public interface UserMapper extends JpaRepository<UserEntity, Integer> {
}
6、编写增删改查业务代码
/**
* @description: UserController
* @date: 2022/3/13 11:00
*/
@RestController
@RequestMapping(value = "user")
public class UserController {
@Autowired
private UserMapper mapper;
@RequestMapping(value = "/add", method = RequestMethod.POST)
public R addUser(@RequestBody UserEntity userEntity) {
UserEntity entity = mapper.save(userEntity);
return R.ok().put("data", entity);
}
@RequestMapping(value = "/delete/{id}", method = RequestMethod.GET)
public R deleteUser(@PathVariable(value = "id") Integer id) {
mapper.deleteById(id);
return R.ok();
}
@RequestMapping(value = "/update", method = RequestMethod.POST)
public R updateUser(@RequestBody UserEntity userEntity) {
UserEntity entity = mapper.save(userEntity);
return R.ok().put("data", entity);
}
@RequestMapping(value = "/getList", method = RequestMethod.GET)
public R getUserList() {
List<UserEntity> userList = mapper.findAll();
return R.ok().put("data", userList);
}
@RequestMapping(value = "/get/{id}")
public R getUserById(@PathVariable(value = "id") Integer id) {
UserEntity entity = mapper.getById(id);
return R.ok().put("data", entity);
}
}
7、使用Postman
工具进行测试
当我们使用一个数据库时,总是希望数据库的内容是可靠的、正确的,但是由于计算机系统的故障(硬件故障、软件故障、网络故障、进程故障和系统故障)影响数据库系统的操作,影响数据库中数据的正确性,甚至破坏数据库,使数据库中全部或部分数据丢失。因此当发生上述故障后,希望能重构这个完整的数据库,这个过程称为数据库恢复,而要进行数据库的恢复必须要有数据库的备份工作。
推荐文章(总结的比较好):https://blog.csdn.net/bugzeroman/article/details/102803866
需要了解的是数据库对语言的划分有四种分别是:
DQL(Data Query Language)
查询语言(重点),包括单表查询、多表查询、子查询、分页查询等DML(Data Manipulation Language)
操作语言,表数据的增、删、改等DDL(Data Definition Language)
定义语言,库与表的管理建表、建库语句DCL(Data Controller Language)
控制语言,事务提交COMMIT
、授予权限GRANT
、撤销权限REVOKE
、事务回滚ROLLBACK
、设置保存点SAVEPOINT
、行锁定LOCK
等因此学习查询语句之前先要准备测试数据,为了尽可能的简单我们使用三张表(LN_ORG / LN_USER / LN_ORG_USER)即可如下所示
-- CREATE TABLE SQL
CREATE TABLE "LN"."LN_ORG" (
"ID" NUMBER(30,0) NOT NULL,
"ORG_NAME" VARCHAR2(255 BYTE),
"MANAGERID" NUMBER(30,0),
"PARENTID" NUMBER(30,0),
CONSTRAINT "LN_ORG_ID_PK" PRIMARY KEY ("ID")
)
TABLESPACE "LN_TEST"
LOGGING
NOCOMPRESS
PCTFREE 10
INITRANS 1
STORAGE (
BUFFER_POOL DEFAULT
)
PARALLEL 1
NOCACHE
DISABLE ROW MOVEMENT
-- INSERT SQL
INSERT INTO LN.LN_ORG(ID, ORG_NAME, MANAGERID, PARENTID) VALUES(1, 'Administration', 200, 1700);
INSERT INTO LN.LN_ORG(ID, ORG_NAME, MANAGERID, PARENTID) VALUES(2, 'Marketing', 201, 1800);
INSERT INTO LN.LN_ORG(ID, ORG_NAME, MANAGERID, PARENTID) VALUES(3, 'Purchasing', 114, 1700);
INSERT INTO LN.LN_ORG(ID, ORG_NAME, MANAGERID, PARENTID) VALUES(4, 'Human Resources', 203, 2400);
INSERT INTO LN.LN_ORG(ID, ORG_NAME, MANAGERID, PARENTID) VALUES(5, 'Shipping', 121, 1500);
INSERT INTO LN.LN_ORG(ID, ORG_NAME, MANAGERID, PARENTID) VALUES(6, 'IT', 103, 1400);
INSERT INTO LN.LN_ORG(ID, ORG_NAME, MANAGERID, PARENTID) VALUES(7, '前端开发', 103, 100);
INSERT INTO LN.LN_ORG(ID, ORG_NAME, MANAGERID, PARENTID) VALUES(8, '后端端开发', 103, 100);
INSERT INTO LN.LN_ORG(ID, ORG_NAME, MANAGERID, PARENTID) VALUES(9, '运维', 103, 100);
-- CREATE TABLE SQL
CREATE TABLE "LN"."LN_USER" (
"ID" NUMBER(10,0) NOT NULL,
"USERNAME" VARCHAR2(255 BYTE),
"PASSWORD" VARCHAR2(255 BYTE),
"FIRST_NAME" VARCHAR2(255 BYTE),
"LAST_NAME" VARCHAR2(255 BYTE),
"PHONE" CHAR(11 BYTE),
"SALARY" NUMBER(8,2),
CONSTRAINT "LN_USER_ID_PK" PRIMARY KEY ("ID"),
CONSTRAINT "LN_USER_ID_DEFERRABLE" CHECK ("ID" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE
)
TABLESPACE "LN_TEST"
LOGGING
NOCOMPRESS
PCTFREE 10
INITRANS 1
STORAGE (
INITIAL 65536
NEXT 1048576
MINEXTENTS 1
MAXEXTENTS 2147483645
BUFFER_POOL DEFAULT
)
PARALLEL 1
NOCACHE
DISABLE ROW MOVEMENT
;
COMMENT ON COLUMN "LN"."LN_USER"."USERNAME" IS '登录名'
-- INSERT SQL
INSERT INTO LN.LN_USER(ID, USERNAME, PASSWORD, FIRST_NAME, LAST_NAME, PHONE, SALARY)
VALUES( LN.S_LN_USER.NEXTVAL, 'Steven', '123', 'Steven', 'King', '515.1234567', 1200);
INSERT INTO LN.LN_USER(ID, USERNAME, PASSWORD, FIRST_NAME, LAST_NAME, PHONE, SALARY)
VALUES(LN.S_LN_USER.NEXTVAL, 'Neena', '123', 'Neena', 'Kochhar', '515.1234568', 17000);
INSERT INTO LN.LN_USER(ID, USERNAME, PASSWORD, FIRST_NAME, LAST_NAME, PHONE, SALARY)
VALUES(LN.S_LN_USER.NEXTVAL, 'Lex', '123', 'Lex', 'De Haan', '515.1234569', 17000);
INSERT INTO LN.LN_USER(ID, USERNAME, PASSWORD, FIRST_NAME, LAST_NAME, PHONE, SALARY)
VALUES(LN.S_LN_USER.NEXTVAL, 'Alexander','123', 'Alexander', 'Hunold', '590.4234567', 9000);
INSERT INTO LN.LN_USER(ID, USERNAME, PASSWORD, FIRST_NAME, LAST_NAME, PHONE, SALARY)
VALUES(LN.S_LN_USER.NEXTVAL, 'Bruce', '123', 'Bruce', 'Ernst', '590.4234568', 6000);
--
-- CREATE TABLE SQL
CREATE TABLE "LN"."LN_ORG_USER" (
"ID" NUMBER(30,0) NOT NULL,
"FK_USER_ID" NUMBER(30,0),
"FK_ORG_ID" NUMBER(30,0),
"REMARK" VARCHAR2(255 BYTE),
CONSTRAINT "LN_ORG_USER_ID_PK" PRIMARY KEY ("ID")
)
TABLESPACE "LN_TEST"
LOGGING
NOCOMPRESS
PCTFREE 10
INITRANS 1
STORAGE (
BUFFER_POOL DEFAULT
)
PARALLEL 1
NOCACHE
DISABLE ROW MOVEMENT
INSERT INTO LN.LN_ORG_USER(ID, FK_USER_ID, FK_ORG_ID) VALUES(1, 4, 7);
INSERT INTO LN.LN_ORG_USER(ID, FK_USER_ID, FK_ORG_ID) VALUES(2, 5, 7);
INSERT INTO LN.LN_ORG_USER(ID, FK_USER_ID, FK_ORG_ID) VALUES(3, 22, 7);
INSERT INTO LN.LN_ORG_USER(ID, FK_USER_ID, FK_ORG_ID) VALUES(4, 24, 8);
/*
一、语法
SELECT 查询列表 FROM 表名;
二、特点
1、查询列表可以是字段、常量、表达式、函数,也可以是多个
2、查询结果是一个虚拟表
三、示例
1、查询单个字段
SELECT 字段名 FROM 表名;
2、查询多个字段
SELECT 字段名, 字段名 FROM 表名;
3、查询所有字段
SELECT * FROM 表名;
4、查询常量
SELECT 常量值 FROM DUAL;
注意:字符型和日期型的常量值必须用单引号引起来,数值型不需要
5、查询函数
SELECT 函数名(实参列表) DUAL;
6、查询表达式
SELECT 100 / 1234 FROM DUAL;
7、起别名
①AS
②空格
8、去重
SELECT DISTINCT 字段名 FROM 表名;
*/
在ORACLE
中需要注意的是:
+ - * /
,SELECT 8 / 4 FROM DUAL; / SELECT SYSDATE - 1 FROM DUAL;
-- 1.起别名可以使用空格或者 AS 关键字为查询结果起别名
SELECT U.LAST_NAME NAME, U.SALARY * 12 AS 年薪 FROM LN.LN_USER U WHERE 1 = 1;
-- 2.查询结果字段名显示默认是大写,我们可以使用 "" 改为小写
SELECT SYSDATE AS "now" FROM DUAL;
-- 3.连接符 || 可以使列与字符连接/合成在一起。类似System.out.println("hello " + "world"); // hello world
SELECT U.FIRST_NAME || ' ' || U.LAST_NAME AS NAME FROM LN.LN_USER U; -- Alexander Hunold
/*
过滤查询:
1 - 语法:SELECT 查询列表 FROM 表名 WHERE 筛选条件;
2 - 筛选条件的分类
1.条件运算符:= > >= < <= <> != 后面两个是不等于
2.逻辑运算符:AND OR NOT
3.模糊查询:LIKE 一般搭配通配符使用,可以判断字符型或数值型。通配符 % 任意多个字符 _ 任意单个字符
4.用于判断NULL:IS NULL / IS NOT NULL
5.其他运算符:BETWEEN ... AND ... / IN(set) / EXISTS / NOT EXISTS
*/
-- 1.例如查询薪资在5000-9000
SELECT U.LAST_NAME, U.SALARY FROM LN.LN_USER U WHERE 1 = 1
AND U.SALARY BETWEEN 5000 AND 9000; -- WHERE U.SALARY >= 5000 AND U.SALARY <= 9000
-- 2.查询用户id是22或23的数据
SELECT U.* FROM LN.LN_USER U WHERE U.ID IN (22, 23); -- WHERE U.ID = 22 OR U.ID = 23
-- 3.查询FIRST_NAME字段第三个字母是e的数据
SELECT U.* FROM LN.LN_USER U WHERE 1 = 1 AND U.FIRST_NAME LIKE '__e%';
-- 4.查询 LAST_NAME 有下划线的数据,注意 %_% 会查出所有数据此时下划线表示某一个字符,因此需要一个转义字符转义下划线
SELECT U.* FROM LN.LN_USER U WHERE U.LAST_NAME LIKE '%\_%' ESCAPE '\';
/*
排序查询 ORDER BY:
1 - 语法
SELECT 查询列表 FROM 表 WHERE 筛选条件 ORDER BY 排序列表 [ASC/DESC]
2 - 特点
1、ASC :升序,如果不写默认升序 DESC:降序
2、排序列表 支持 单个字段、多个字段、函数、表达式、别名
3、ORDER BY的位置一般放在查询语句的最后(除 ROWNUM 语句之外)
*/
-- 查询前端开发部门(部门id=7)的用户数据并且薪资降序排列
SELECT
U.LAST_NAME,
O.ORG_NAME,
U.SALARY
FROM LN.LN_USER U
INNER JOIN LN.LN_ORG_USER OU ON OU.FK_USER_ID = U.ID
INNER JOIN LN.LN_ORG O ON O.ID = OU.FK_ORG_ID
WHERE 1 = 1
AND O.ID = 7
ORDER BY U.SALARY DESC;
我们发现SALARY
有相同的情况!针对这种情况我们还可以继续进行二级排序或n级排序
SELECT
U.ID,
U.LAST_NAME,
O.ORG_NAME,
U.SALARY
FROM LN.LN_USER U
INNER JOIN LN.LN_ORG_USER OU ON OU.FK_USER_ID = U.ID
INNER JOIN LN.LN_ORG O ON O.ID = OU.FK_ORG_ID
WHERE 1 = 1
AND O.ID = 7
ORDER BY U.SALARY DESC, U.ID DESC; -- 二级排序
函数概述:类似于Java中的方法,提高重用性与隐藏实现细节。
/*
1、字符函数
CONCAT(ch1, ch2) 连接 与 || 功能类似
SUBSTR(ch, pos, length) 截取子串
UPPER(ch) 变大写
LOWER(ch) 变小写
REPLACE(source, search_str, replace_str) 替换
LENGTH(ch) 获取字节长度
TRIM(trim_source) 去前后空格
LPAD(expr1, n, expr2) 左填充(不够了才填充)
RPAD(expr1, n, expr2) 右填充(不够了才填充)
INSTR(str, substr) 获取E在STR第一次出现的索引下标
*/
SELECT CONCAT('1', '2') FROM DUAL; -- 12
SELECT SUBSTR('12ABC', 0, 2) FROM DUAL; -- 12
SELECT UPPER('abc') FROM DUAL; -- ABC
SELECT LOWER('ABC') FROM DUAL; -- abc
SELECT REPLACE('AAAA', 'A', 'B') FROM DUAL; -- BBBB
SELECT LENGTH('IPHONE') FROM DUAL; -- 6
SELECT TRIM(' ABC ') FROM DUAL; -- ABC
SELECT LPAD('MC', 10, '+') FROM DUAL; -- ++++++++MC
SELECT INSTR('ABC', 'B') FROM DUAL; -- 2
/*
2、数学函数
CEIL(n) 向上取整
ROUND(n, int) 四舍五入
MOD(n1, n2) 取模
FLOOR(n) 向下取整
TRUNC(date, fmt) 截断
*/
SELECT CEIL(3.2) FROM DUAL; -- 4
SELECT ROUND(5.1666, 2), ROUND(5.1666) FROM DUAL; -- 5.17 5
SELECT MOD(9, 2) FROM DUAL; -- 1
SELECT FLOOR(2.9) FROM DUAL; -- 2
SELECT TRUNC(1.666, 2) FROM DUAL; -- 1.66
/*
3、日期函数(日期 + 时间)
1.SYSDATE 系统日期变量 SELECT SYSDATE FROM DUAL;
2.在日期上加上或减去一个数字结果任为日期。例如 SELECT SYSDATE - 1 FROM DUAL; -- 获取昨天这个点的日期
3.两个日期相减返回日期之间相差的 天数(日期不允许做加法运算,无意义)
4.可以使用数字除24来向日期中加上或减去天数
MONTHS_BETWEEN(date1, date2) 两个日期相差的月数
ADD_MONTHS(date, int) 向指定日期中加上若干月数
NEXT_DAY(date, ch) 指定日期的下一个星期 * 对应的日期
LAST_DAY(date) 本月的最后一天
ROUND(date, fmt) 日期的四舍五入 fmt 可以是 MONTH/YEAR
*/
SELECT ROUND(TO_DATE('2022-03-16 19:31:54', 'yyyy-mm-dd hh24:mi:ss'), 'MONTH') FROM DUAL; -- 2022-04-01 00:00:00
/*
4、转换函数
TO_CHAR(x) 转 char
TO_DATE(ch, fmt) 转 date
TO_NUMBER(expr, fmt) 转 number
*/
SELECT TO_CHAR(SYSDATE, 'yyyy/mm/dd hh24/mi/ss') FROM DUAL; -- 2022/03/16 19/49/41
SELECT TO_DATE('2022-03-16 19:31:54', 'yyyy-mm-dd hh24:mi:ss') FROM DUAL;
/*
5、通用函数(这些函数适用于任何数据类型,同时也适用于空值)
NVL(expr1, expr2) 等价于 expr1 == null ? expr2 : expr1
NVL2(expr1, expr2, expr3) expr1 == null 返回expr3,否侧返回expr2
COALESCE(expr1, ... exprn) 与NVL相比的优点在于 COALESCE可以处理交替的多个值,如果第一个表达式为空,则返回下一个表达式
NULLIF(expr1, expr2) 相等返回NULL,不等返回 expr2
*/
SELECT NVL2(1, 'expr2', 'expr3') FROM DUAL; -- expr2
SELECT NVL2(NULL, 'expr2', 'expr3') FROM DUAL; -- expr3
在编程语言中都会有if else
的条件表达式,在SQL中我们也有相应的条件表达式来处理特殊的场景,在SQL中也称为流程控制函数。
/*
在SQL中,我们可以使用以下两种方式处理 if else 逻辑
CASE WHEN 语句
DECODE(expr, [search, result]*, default) 函数
CASE WHEN 的具体语法:
CASE 变量或表达式或字段
WHEN comparison_expr1 THEN return_expr1
[WHEN comparison_expr2 THEN return_expr2
WHEN comparison_expr3 THEN return_expr3
ELSE else_expr]
END
或者是
CASE
WHEN 条件1 THEN 值1
WHEN 条件2 THEN 值2
...
ELSE 值N
END
*/
/*
CASE WHEN案例:查询员工的工资的情况
如果工资 > 20000,显示A级别
如果工资 > 15000,显示B级别
如果工资 > 10000,显示C级别
否则,显示D级别
*/
SELECT SALARY,
CASE
WHEN SALARY > 20000 THEN 'A'
WHEN SALARY > 15000 THEN 'B'
WHEN SALARY > 10000 THEN 'C'
ELSE 'D'
END AS 工资级别
FROM LN.LN_USER;
/*
DECODE(expr, [search, result]*, default) 使用案例:查询员工的工资,要求
部门号=30,显示的工资为1.1倍
部门号=40,显示个工资为1.2倍
部门号=50,显示的工资为1.3倍
其他部门,显示的工资为原工资
*/
SELECT
U.LAST_NAME,
DECODE(O.ID,
30, U.SALARY * 1.1,
40, U.SALARY * 1.2,
50, U.SALARY * 1.3,
U.SALARY) AS SALARY
FROM LN.LN_USER U
INNER JOIN LN.LN_ORG_USER OU ON OU.FK_USER_ID = U.ID
INNER JOIN LN.LN_ORG O ON O.ID = OU.FK_ORG_ID
WHERE 1 = 1
分组函数用作统计使用,又称为聚合函数或统计函数或函数。作用于一组数据,并对一组数据返回一个值。
/*
1、常见的分组函数有如下几种
MAX(expr) 最大值
MIN(expr) 最小值
SUM(expr) 和
AVG(expr) 平均值
COUNT(expr) 计算个数
2、分组函数支持的类型说明
SUM 和 AVG 一般用于处理数值型
MAX MIN COUNT 可以处理任何数据类型
3、其他说明
分组函数都忽略NULL
分组函数都可以搭配 DISTINCT 使用,实现去重的统计 SELECT SUM(DISTINCT U.SALARY) FROM LN.LN_USER U;
COUNT(字段) 统计该字段非空值得个数
COUNT(*) 统计结果集的行数
和分组函数一同查询的字段,要求是GROUP BY后出现的字段
*/
SELECT SUM(SALARY) FROM LN.LN_USER;
SELECT AVG(SALARY) FROM LN.LN_USER;
SELECT MIN(SALARY) FROM LN.LN_USER;
SELECT MAX(SALARY) FROM LN.LN_USER;
SELECT COUNT(SALARY) FROM LN.LN_USER;
多表查询又称连接查询,当查询的字段来自于多个表时,就会用到连接查询。连接查询需要注意的是避免产生笛卡尔乘积(表1有n行,表2有n行,查询结果就是 n * m 行)造成冗余数据查询。大多数造成笛卡尔乘积的原因都是没有有效的连接条件,因此使用连接查询时一定要添加有效的连接条件(等值连接)。
-- 笛卡尔乘积现象举例
SELECT
U.LAST_NAME,
O.ORG_NAME
FROM LN.LN_USER U, LN.LN_ORG O
WHERE 1 = 1
-- 总共输出 63 条记录
SELECT COUNT(*) FROM LN.LN_USER; -- 7
SELECT COUNT(*) FROM LN.LN_ORG; -- 9
-- 这里我们使用第三张表维护了它们之间的关系,所以正确的写法如下
-- SQL92标准写法(仅仅支持内连接、往后的SQL都不推荐这种写法)
SELECT
U.LAST_NAME,
O.ORG_NAME
FROM LN.LN_USER U, LN.LN_ORG O, LN.LN_ORG_USER OU
WHERE 1 = 1
AND U.ID = OU.FK_USER_ID
AND O.ID = OU.FK_ORG_ID
/*
关于SQL92标准的等值连接补充:
1. 多表等值连接的结果为多表的交集部分
2. n表连接,至少需要n-1个连接条件(例如上述例子三张表相连,需要有2个连接条件)
3. 多表的顺序没有要求
4. 一般需要为表起别名
5. 可以搭配前面介绍的所有子句使用,如排序、分组、筛选
*/
/*
SQL99标准写法(支持多种连接方式,推荐写法)
语法如下
SELECT 查询列表 FROM 表1 别名 [连接类型] JOIN 表2 别名 ON 连接条件 [WHERE 筛选条件]
[GROUP BY 分组]
[HAVING 筛选条件]
[ORDER BY 排序列表]
其中 连接类型 又有多种:
1. 内连接(等值 & 非等值):INNER
2. 外连接:OUTER
左外:LEFT [OUTER]
右外:RIGHT [OUTER]
全外:FULL [OUTER]
3. 交叉连接:CROSS
*/
# 因此SQL99标准写法如下
# 等值连接
SELECT
U.LAST_NAME,
O.ORG_NAME
FROM LN.LN_USER U
INNER JOIN LN.LN_ORG_USER OU ON OU.FK_USER_ID = U.ID
INNER JOIN LN.LN_ORG O ON O.ID = OU.FK_ORG_ID
WHERE 1 = 1
/*
关于SQL99标准的等值连接补充:
1. 添加排序、分组、筛选
2. INNER 可以省略
3. 筛选条件放在WHERE后面,连接条件
4. INNER JOIN连接和SQL192语法中的等值连接效果是一样的,都是查询多表的交集
*/
# 非等值连接
# 查询员工的工资级别
SELECT SALARY, GRADE_LEVEL FROM EMPLOYEES E
INNER JOIN JOB_GRADES G ON E.SALARY BETWEEN G.LOWEST_SAL AND G.HIGHEST_SAL;
在实际开发中,我们使用INNER JOIN(INNER 可以省略)
和LEFT JOIN
就能适应百分之90的场景。更多JOIN
连接理论参照文章:https://blog.csdn.net/m0_46357847/article/details/120210352
内外连接补充:
/*
外连接的应用场景:用于查询一个表中有,另一个表没有的记录
特点:
1.外连接的查询结果为主表中的所有记录
如果从表中有和它匹配的,则显示匹配的值
如果从表中没有和它匹配的,则显示NULL值
外连接查询结果 = 内连接结果 + 主表中有而从表没有的记录
2. 左外连接,LEFT JOIN 左边的是主表
右外连接,RIGHT JOIN 右边的是主表
3. 左外和右外交换两个表的顺序,可以实现同样的效果
4. 全外连接 = 内连接的结果 + 表1中有但表2没有的 + 表2中有但表1没有的
*/
# 案例:查询哪个部门没有员工
# 左外
SELECT D.*, E.EMPLOYEE_ID FROM DEPARTMENTS D
LEFT OUTER JOIN EMPLOYEES E ON D.DEPARTMENT_ID = E.DEPARTMENT_ID
WHERE E.EMPLOYEE_ID IS NULL;
# 右外
SELECT D.*, E.EMPLOYEE_ID FROM EMPLOYEE E
RIGHT OUTER JOIN DEPARTMENTS D ON D.DEPARTMENT_ID = E.DEPARTMENT_ID
WHERE E.EMPLOYEE_ID IS NULL;
# 交叉连接
SELECT B.*, BO.*
FROM BEAUTY B
CROSS JOIN BOYS BO; -- 笛卡尔积