Oracle数据库,使用记录。更多笔记参考数据库-Mysql,语法相似。
时间
时间范围
1 |
#1.时间转换为字符,再比较大小 |
字符转时间
时间函数to_date():
1 |
#更新project_turn_delay表转固时间(最新审批时间)、转固单号(见下边的ProjectTurnBO类) |
最新/最旧
时间字段
针对时间字段,查询最新/最旧的一条数据。
案例一,对时间字段进行排序
案例:获取最新转固单审批通过日期,在wf_taskhis表中查询。查询结果(排序后,最新时间位于第一条数据)作为新表,使用rownum查询第一条。
1 |
wf_taskhis 任务实例历史表,查询最新的审批时间 |
案例二,对时间字段使用max函数、min函数
案例:对表wf_taskhis进行分组处理,取最新的处理时间check_date、功能ID、数据ID作为新表(起别名wf_taskhis与原表名相同也是可以的),再参与asset_handle_det表、asset_handle表的查询,后边查询出来的check_date字段数据就是最新的时间了。
1 |
(select fun_id,data_id,max(check_date) check_date from wf_taskhis group by fun_id,data_id) wf_taskhis |
完整SQL如下:(字段过多这里使用一个*代替了所有字段)
1 |
select * |
row_number函数
使用row_number()函数,查询最新一条数据。
针对SELECT语句返回的每一行,从1开始编号,赋予其连续的编号。
partition by用于给结果集分组,如果没有指定那么它把整个结果集作为一个分组。(partition by能返回一个分组中的多条记录)
1 |
update asset_center set (center_name,memo,center_name_en) = ( |
rowid
oracle数据库的表中的每一行数据都有一个唯一的标识符,或者称为rowid。对rowid使用max函数、min函数即可实现查询最新或最旧的一条记录。
1 |
-- 查看数据 |
基于时刻的查询
若表中没有记录数据插入时间的列,可以借助as of timestamp
实现基于时间的查询(Oracle快照,近期数据备份)。
- 查看某一时间点的数据
- 查看某一时间点后新增的记录
1 |
select * from mytable as of timestamp to_timestamp('2022/1/6 11:21:00','yyyy-MM-dd hh24:mi:ss') |
as of timestamp,可用于恢复数据。详情见后边的笔记
时间增减
月份增加,可以使用函数add_months()
1 |
#sysdate,系统当前时间。add_months(sysdate,1),时间增加1月,例如:2022-2-17 =》2022-3-17 |
分页
分页rownum,MySQL中为limit:
1 |
-- 案例一: |
视图
视图:
1 |
#Oracle数据库 |
create or replace view - 若数据库中已经存在这个名字的视图就替换它,若没有则创建
create or replace force view - 同上,不同点在于会强制创建视图。正常情况下,如果基表不存在,创建视图就会失败。但是可以使用force选项强制创建视图(前提:创建视图的语句没有语法错误!),此时该视图处于失效状态。
create - 不进行判断,若数据库中已有则报错
联表
查询结果插入/更新到另一个表。
1 |
#插入 |
联表更新/插入
普通update
1 |
-- 注意,更新的是t1整张表,需要按具体情况添加where条件 |
MERGE关键字
MERGE关键字是一个神奇的DML关键字,它能将INSERT,UPDATE,DELETE等操作并为一句,根据与源表联接的结果,对目标表执行插入、更新或删除操作。
案例一:将查询结果更新到另一个表
1 |
-- 查询 已审批通过的在建工程转固报销单 |
案例二:如果存在数据则更新否则新增
1 |
#建议使用(Oracle 9以上版本): |
字符相关
长度
在select语句中,使用函数length(),获取数据(内容)长度。也可用于order by中。
1 |
select length(user_code) 账号长度,user_code 账号,user_name 用户名 from sys_user |
去除空格
批量去除字段值空格
1 |
#去除左边空格 |
截取字符串
substr 函数:截取字符串
语法:SUBSTR(string,start, [length])
string:表示源字符串,即要截取的字符串。
start:开始位置,从1开始查找。如果start是负数,则从string字符串末尾开始算起。
length:可选项,表示截取字符串长度。不写则截取到最后。
instr 函数:返回子字符串在源字符串中的位置
- 语法:INSTR(string,child_string,[start],[show_time])
- string:表示源字符串。
- child_string:子字符串,即要查找的字符串。
- start:可选项,开始位置,默认从1开始。如果为负数,则从右向左检索。
- show_time:可选项,表示子字符串第几次出现在源字符串当中,默认第1次,负数则报错。
- 注意:
- Oracle中字符串位置是从 1 开始,而不是0
- 没有匹配到目标子字符串则返回0
例如:将仓库名称“xxx周转库”,修改为“xxx库存”
1 |
select house_name 仓库,SUBSTR(house_name, 1, INSTR (house_name, '备件周转库', 1, 1)-1)||'库存' 仓库,sp_code 物资编码,sum(store_num)||'个' 库存数量 from sp_store where data_type = '1' group by house_name,sp_code |
注意看下图的第一、二列:
原文链接:CSDN
大小写
1 |
#大小写转换 |
拼接
同一行的记录,将不同字段的数据拼接在一起输出。可以使用连接运算符||
函数实现。
不同行的记录,通过分组函数聚合后,将同一字段的数据拼接在一起。可以使用listagg函数、xmlagg函数实现,见下边的列转行部分。
连接运算符
||
连接运算符
1 |
select '姓名:' || c.stuname || ', 课程:' || b.coursename || ', 成绩:' || a.score || '分数' as sxcj |
列转行
列转行,即多条记录转换为一条记录,使多行显示在一行。
listagg函数
- 该函数的返回值是varchar类型,最大长度4000,超过4000的话,数据库查询会报错【ORA-01489:字符串连接的结果过长】
xmlagg函数
- 该函数的返回值是clob类型
- 在Java中需要用java.sql.Clob类,进行数据的接收与转换
原文链接:函数Listagg与xmlagg区别
listagg函数
原文链接:Oracle函数之LISTAGG
1 |
//用法: |
案例:
案例一
1 |
#查询仓库名 |
案例二
1 |
select a.user_name 姓名,a.user_code 账号,a.dept_name 部门,a.add_date 开通日期,listagg(a.role_name,';')within group(order by a.role_name) 角色 |
查询结果:
xmlagg函数
用法:xmlagg(xmlparse(content 聚合的字段||’,’ wellformed) order by 分组的字段).getclobval() as 新的列名
案例:
1 |
select a.user_name 姓名,a.user_code 账号,a.dept_name 部门,a.add_date 开通日期,xmlagg(xmlparse(content a.role_name||';' wellformed) order by a.role_name).getclobval() 角色 |
查询结果与上边listagg函数的案例二相同。
case when then
案例见这里。
替换
见后边的正则表达式部分。
特殊字符
英文单引号'
- 单引号
'
为特殊字符,在单引号包裹的字符中若有单引号需要对其进行转义,只需多写一个单引号即可,两个单引号代表一个单引号(转义)。 - 使用运算符
||
进行拼接。例如:apple's => 'apple'||''''||'s'
,四个'
代表加了单引号包裹的单引号。或'apple''s'
英文双横杠--
- 使用运算符
||
进行拼接。例如:A--302 => 'A'||'-'||'-'||'302'
,四个'
代表加了单引号包裹的单引号
英文分号;
- 使用运算符
||
进行拼接。若数据为中文可考虑将分号替换为中文分号;
like过滤
普通like
笔记摘自:CSDN
LIKE比较运算符:
- 可以使用以下两个通配符
%
和_
,其中%
代表零个或多个字符,_
代表一个且只能是一个字符。 - 如果要查询的数据中含有
_
或%
,需使用转义(escape)关键字 - 适用于一个过滤条件
1 |
-- 示例:查询c_where字段中,包含 '101303%' 的数据。 |
like函数
笔记摘自:字段like多个条件(or关系)简写
使用like过滤时有多个条件,简写如下:
1 |
select * from tablex where REGEXP_LIKE(字段名, '(匹配串1|匹配串2|...)') ;//全模糊匹配 |
案例:
1 |
-- OT事业部 所有账号 |
查看表/视图结构
1 |
SELECT DBMS_METADATA.GET_DDL('TABLE','表名 大写') FROM DUAL; |
查看表/视图数量
1 |
-- 表数量 |
查询带有某个字段的所有表
1 |
-- lower()函数,将大写转换为小写 |
查询某个表
只知道表名中的几个字符,查找表
1 |
-- 查找表名中包含“SP_CAT”的表(大写) |
查看表数据大小
1 |
select segment_name, sum(bytes) / 1024 / 1024 from user_extents where segment_type='TABLE' and segment_name='SP_CATALOG' group by segment_name |
整合
查询表,并显示表数据大小
1 |
select a.last_analyzed 最近一次更新时间,a.table_name 表名,'0'||b.mysize||'M' 数据量,a.tablespace_name 表空间,b.owner 所有者 from user_tables a,( |
统计
常用函数:
AVG: 计算平均数
MAX: 获取最大值
MIN: 获取最小值
SUM: 计算总和
COUNT: 计算数据条数
group by
总结:
select子句后的任一非聚合函数字段都应来源于group by 分组语句后,否则语法会编译不通过。
可以把分出来的组理解为试图,所有的其他操作都基于这张视图。
当在一个SQL中同时使用where和group by和having子句时,其执行顺序为:where>group by>having。
同时where子句作用于表或者视图,having子句作用于组,having子句必须作用在group by之后。
partition by
partition by用于给结果集分组,如果没有指定那么它把整个结果集作为一个分组。案例见上边的基于时间查询。
小结:
group by 和 partition by 都有分组统计的功能,但是partition by并不具有group by的汇总功能。partition by统计的每一条记录都存在,而group by将所有的记录汇总成一条记录(类似于distinct EmpDepartment 去重)。partition by可以和聚合函数结合使用,同时具有其他高级功能。
case when then
case when then用于解决字符映射:
1 |
1.简单Case函数写法(注意sex的位置) |
参考这篇博客:select case when if 的一些用法
获取重复数据
笔记摘自:oracle查询重复数据方法
分组函数group by
,结合having count(*) > 1
查询表中重复的数据(指定单个字段)。
1 |
-- 通过 table_id,name,age 判断是否相同 |
案例1
sum求和函数、group by分组、case when then的使用:
1 |
select acc_name 资产分类,sum(case when device_id is null then 0 else 1 end) 资产数量,sum(ori_value) 资产金额 from asset_card where acc_name is not null group by acc_name |
案例2-列转行
n条记录显示为1条
n条记录显示为1条,上边的查询结果显示为下边的样子。
需求:物资采购申请明细中需显示物料对应的十个周转库的库存数量,数据表不用新增字段,写视图关联查询;
思路:使用group by分组,使用sum函数统计,使用case when then分组统计
查询周转库库存sp_store(data_type = ‘1’),先根据仓库分组(group by house_id)再根据物资编码分组(group by house_id,sp_code),分别统计十个周转库中相应物资的库存数量(sum函数结合case函数)
1
2
3
4
5
6
7
8
9
10
11
12
13
14select sp_name,sp_code,
sum(case when house_id = '1030' then store_num else 0 end) store_1,
sum(case when house_id = '1031' then store_num else 0 end) store_2,
sum(case when house_id = '1028' then store_num else 0 end) store_3,
sum(case when house_id = '1029' then store_num else 0 end) store_4,
sum(case when house_id = '1034' then store_num else 0 end) store_5,
sum(case when house_id = '1025' then store_num else 0 end) store_6,
sum(case when house_id = '1026' then store_num else 0 end) store_7,
sum(case when house_id = '1027' then store_num else 0 end) store_8,
sum(case when house_id = '1032' then store_num else 0 end) store_9,
sum(case when house_id = '1033' then store_num else 0 end) store_10
from sp_store where data_type = '1' group by house_id,sp_code,sp_name
#group by后边添加sp_name,是为了测试时显示出物资名称第一步的查询结果,每一行(条)记录为某周转库中某一物资的数量。以物资编码作为条件过滤(例如:where sp_code = ‘SF72077’),可得到各周转库中该物资的数量(n条记录),再进行求和(group by sp_code 结合sum函数)可得各周转库中该物资的数量(n条变1条记录)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15select sum(store_1) 冲压厂冲压一部库存,
sum(store_2) 冲压厂冲压二部库存,
sum(store_3) 车架厂车架一部库存,
sum(store_4) 车架厂车架二部库存,
sum(store_5) 质量管理部库存,
sum(store_6) 总装厂库存,
sum(store_7) 车身厂涂装部库存,
sum(store_8) 车身厂车身部库存,
sum(store_9) 一厂区能源与动力管理科库存,
sum(store_10) 欧曼工厂能源与动力管理科库存
from (
第一步查询结果,见上边select语句
)
where sp_code = 'SF72077'
group by sp_code第一步查询结果作为新表,通过物资编码关联表pur_apply_det(物资采购明细表,保存有物资ID、物资编码等信息)。整合上边一、二步,视图定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31CREATE OR REPLACE FORCE VIEW "V_PUR_APPLY_DET3" AS
select b.apply_detid,
sum(si.store_1) store_1,
sum(si.store_2) store_2,
sum(si.store_3) store_3,
sum(si.store_4) store_4,
sum(si.store_5) store_5,
sum(si.store_6) store_6,
sum(si.store_7) store_7,
sum(si.store_8) store_8,
sum(si.store_9) store_9,
sum(si.store_10) store_10
from (
select sp_code,
sum(case when house_id = '1030' then store_num else 0 end) store_1,
sum(case when house_id = '1031' then store_num else 0 end) store_2,
sum(case when house_id = '1028' then store_num else 0 end) store_3,
sum(case when house_id = '1029' then store_num else 0 end) store_4,
sum(case when house_id = '1034' then store_num else 0 end) store_5,
sum(case when house_id = '1025' then store_num else 0 end) store_6,
sum(case when house_id = '1026' then store_num else 0 end) store_7,
sum(case when house_id = '1027' then store_num else 0 end) store_8,
sum(case when house_id = '1032' then store_num else 0 end) store_9,
sum(case when house_id = '1033' then store_num else 0 end) store_10
from sp_store where data_type = '1' group by house_id,sp_code
) si,pur_apply_det b where b.sp_code = si.sp_code
group by b.apply_detid,b.sp_code
#通过物资采购申请明细表的主键关联视图
select * from V_PUR_APPLY_DET3 where apply_detid = 'jxstar15251135'
select b.*,a.* from V_PUR_APPLY_DET3 a,pur_apply_det b where a.apply_detid = b.apply_detid and b.apply_detid = 'jxstar15251135'
数据恢复
恢复Delete
恢复少量数据
使用oracle提供的闪回方法进行数据恢复,适用于delete删除(少量数据)方式。
查看快照中数据(能查询出数据,才能恢复):
1 |
#as of timestamp - 应用Flashback Query查询过去的数据(基于时间的查询) |
恢复数据:
将从快照中查询出来的数据插入原表中,注意要保证主键不重复。
1 |
insert into 表名 (select * from 表名 as of timestamp to_timestamp('删除时间点','yyyy-mm-dd hh24:mi:ss')); |
扩展:基于时间的查询,查看某一时间点后新增的记录
1 |
select * from mytable where my_id not in ( |
恢复整张表
闪回整个表来恢复误删除的数据,但仅适用于表结构没有发生改变且用户有flash any table权限的情况下。
当需要使用flashback table功能时,需要先打开row mvoement的选项(启用表的行移动),否则使用该功能会报错。
1 |
#将row movement设置为enable |
恢复Drop
因为oracle数据库在删除表时会将删除信息存放于某虚拟“回收站”中而非直接清空,再此种状态下数据库标记该表的数据库为“可以复写”,所以在该块未被重新使用前依然可以恢复数据。该方法多用于drop删除。
首先需要查询user_table视图找到被删除的表:
1 |
#查看所有未被drop的表 |
original_name为原表名,table_name和object_name就是回收站中的存放表名,如果明确原表的名称可以通过以下命令恢复:
1 |
#将表恢复到drop前 |
如果不知道原表名称可以直接按照回收站中的表名称将数据恢复,通过表数据确定是否是要恢复的表,最后对数据表进行重命名即可。
1 |
#将表恢复到新表(重新创建一个表) |
恢复数据库
利用oracle数据库的闪回功能可以将数据库恢复到过去某一状态,语法如下:
1 |
SQL>alter database flashback on |
未测试过。上边的方法足够恢复数据使用。
扩展
1、采用truncate方式进行截断。(但不能进行数据回恢复了)
2、在drop时加上purge选项:drop table 表名 purge
该选项也可以通过删除recyclebin区域来永久性删除表 ,原始删除表drop table emp cascade constraints
purge table emp;
删除当前用户的回收站:
purge recyclebin;
删除全体用户在回收站的数据:
purge dba_recyclebin_frombyte.com
锁表与解锁
锁表原因:
- 锁表的原理:数据库使用独占式封锁机制,当执行上面的语句时,对表进行锁住,直到发生commite 或者 回滚 或者退出数据库用户
- 主要的锁表原因有:
- A程序执行了对 tableA 的 insert ,并还未 commite时,B程序也对tableA 进行insert 则此时会发生资源正忙的异常 就是锁表
- 锁表常发生于并发而不是并行(并行时,一个线程操作数据库时,另一个线程是不能操作数据库的,cpu 和i/o 分配原则)
- 减少锁表的概率方法:
- 减少insert 、update 、delete 语句执行 到 commite 之间的时间。具体点批量执行改为单个执行、优化sql自身的非执行速度
- 如果异常对事务进行回滚
查看被锁表信息:
1 |
#object_name 被锁的表名 |
杀掉锁表进程:
1 |
#后边参数分别为SID和SERIAL# |
查看什么语句导致锁表的:
1 |
select l.session_id sid, |
查看有几个表被锁:
1 |
select count(*) from v$locked_object; |
not exists子查询
案例一:
用户表sys_user、用户角色表sys_user_role、角色表sys_role
查询 用户表中stafftype = ‘01’且没有角色“项目管理员”的 用户。
1 |
select * from sys_user |
案例二:
在插入数据前,查询表中是否已有再决定是否要执行插入操作。
若sys_user_data表中该用户已有dtype_data为1034的记录则不插入数据:
1 |
insert into sys_user_data (user_id,dtype_id,dtype_data,display,has_sub,add_date,add_userid) |
正则表达式
笔记摘自:CSDN
Oracle中使用正则表达式:
regexp_like,用于条件表达式,和 like 类似
regexp_substr,用于截取符合正则表达式描述的字符子串
regexp_instr,用于标定符合正则表达式的字符子串的开始位置
regexp_replace,用于替换符合正则表达式的字符串
1 |
#案例: |
存储过程
笔记摘自:CSDN
存储过程:procedure
定义
定义简单存储过程:
1 |
-- 案例1 |
定义稍复杂一点的存储过程:
1 |
-- 更新部门(名称、id) |
调用
1 |
#执行存储过程 |
删除
1 |
#删除存储过程 |