0%

Oracle笔记

Oracle数据库,使用记录。更多笔记参考数据库-Mysql,语法相似。

时间

时间范围

1
2
3
4
#1.时间转换为字符,再比较大小
and to_char(a.scan_date,'yyyymmdd') >= '20211215'

#2.字符转换为时间,比较时间大小。见下边的“字符转时间”笔记

字符转时间

时间函数to_date():

1
2
#更新project_turn_delay表转固时间(最新审批时间)、转固单号(见下边的ProjectTurnBO类)
update project_turn_delay set TURN_DATE = to_date(?,'yyyy-MM-dd hh24:mi:ss'),TURN_CODE = ? where delay_id = ?

最新/最旧

时间字段

针对时间字段,查询最新/最旧的一条数据。

案例一,对时间字段进行排序

案例:获取最新转固单审批通过日期,在wf_taskhis表中查询。查询结果(排序后,最新时间位于第一条数据)作为新表,使用rownum查询第一条。

1
2
3
4
5
6
7
8
wf_taskhis    任务实例历史表,查询最新的审批时间

fun_id 功能ID(功能主键,一般为表名)
data_id 数据ID,(一般为表主键)
check_date 处理时间,即我们需要的审批时间

#查询 最新的审批时间 作为 审批通过日期
select myres.* from ( select check_date from wf_taskhis where fun_id = 'project_turn_delay' and data_id = ? order by check_date desc ) myres where rownum = 1

案例二,对时间字段使用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
2
3
4
5
6
7
8
select *
from asset_handle_det,(select fun_id,data_id,max(check_date) check_date from wf_taskhis group by fun_id,data_id) wf_taskhis,asset_handle
where (
asset_handle_det.handle_id = asset_handle.handle_id
and asset_handle.auditing = '3'
and wf_taskhis.data_id = asset_handle_det.handle_id
and wf_taskhis.fun_id= 'asset_handle'
);

row_number函数

使用row_number()函数,查询最新一条数据。

针对SELECT语句返回的每一行,从1开始编号,赋予其连续的编号。

partition by用于给结果集分组,如果没有指定那么它把整个结果集作为一个分组。(partition by能返回一个分组中的多条记录)

1
2
3
4
5
6
7
8
9
update asset_center set (center_name,memo,center_name_en) = (
select center_name,memo,center_name_en from (
-- 根据时间排序,编号,取第一条(排序后编号为1的即最新的一条。后边还有where条件rn=1)
select log_cost_center.*, row_number()over(partition by center_code order by add_date desc) rn
from log_cost_center
)
where rn=1 and center_code=asset_center.center_code
)
where exists(select 1 from log_cost_center where center_code=asset_center.center_code)

rowid

oracle数据库的表中的每一行数据都有一个唯一的标识符,或者称为rowid。对rowid使用max函数、min函数即可实现查询最新或最旧的一条记录。

1
2
3
4
-- 查看数据
select rowid,mytable2.* from mytable2
-- 获取rowid最大(最新)的一条
select max(rowid),table_id,name,add_date from mytable2 group by table_id,name,add_date

基于时刻的查询

若表中没有记录数据插入时间的列,可以借助as of timestamp实现基于时间的查询(Oracle快照,近期数据备份)。

  • 查看某一时间点的数据
  • 查看某一时间点后新增的记录
1
2
3
4
5
select * from mytable as of timestamp to_timestamp('2022/1/6 11:21:00','yyyy-MM-dd hh24:mi:ss')

select * from mytable where my_id not in (
select my_id 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
2
#sysdate,系统当前时间。add_months(sysdate,1),时间增加1月,例如:2022-2-17 =》2022-3-17
update project_contract set qual_expire = add_months(sysdate,1) where contract_id = 'jxstar7252651'

分页

分页rownum,MySQL中为limit:

1
2
3
4
5
6
7
8
9
-- 案例一:
#查询 最新的审批时间 作为 审批通过日期
select myres.* from ( select check_date from wf_taskhis where fun_id = 'project_turn_delay' and data_id = ? order by check_date desc ) myres where rownum = 1

-- 案例二:
select * from (
select factory_name,factory_id,dept_id,dept_name,local_name,local_id,team_name,team_id,rownum rn
from wpa_card
) where rn between 10 and 20

视图

视图:

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
#Oracle数据库

#测试
#round函数,四舍五入
#to_number将日期转换为数字(两日期相减,求相隔天数)
#outday 超期天数
select round(TO_NUMBER(sysdate - PLAN_TURNDATE)) as outday,PROJECT_CONTRACT.*
from PROJECT_CONTRACT
where AUDITING in ('1','3')
and CONTRACT_STATUS != '3'
and PLAN_TURNDATE is not null
and PLAN_TURNDATE < sysdate

#创建视图
create or replace force view v_cont_turn_overdue
as
select round(TO_NUMBER(sysdate - PLAN_TURNDATE)) as outday,PROJECT_CONTRACT.*
from PROJECT_CONTRACT
where AUDITING in ('1','3')
and CONTRACT_STATUS != '3'
and PLAN_TURNDATE is not null
and PLAN_TURNDATE < sysdate

#测试,视图
select * from v_cont_turn_overdue

create or replace view - 若数据库中已经存在这个名字的视图就替换它,若没有则创建

create or replace force view - 同上,不同点在于会强制创建视图。正常情况下,如果基表不存在,创建视图就会失败。但是可以使用force选项强制创建视图(前提:创建视图的语句没有语法错误!),此时该视图处于失效状态。

create - 不进行判断,若数据库中已有则报错

联表

查询结果插入/更新到另一个表。

1
2
3
4
5
6
7
#插入
#insert into t1 (col1,col2) (select col1,col2 from t2 where ...)
#insert into t1 select * from t2 where ...
insert into equ_scan_det(device_code,device_name,dept_name,dev_memo)(select device_code,device_name,dept_name,dev_memo from asset_card where asset_code = '001000000843')

#更新(可加where条件)
update t1 set (nickname,playNum)=(select nickname,playNum from t2 where t1.id=t2.id) where ... ;

联表更新/插入

笔记摘自:csdn知乎

普通update

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 注意,更新的是t1整张表,需要按具体情况添加where条件
update t1 set (nickname,playNum)=
(select nickname,playNum from t2 where t1.id=t2.id);


-- 案例:
-- 只更新 wpa_num > 1 的数据
update wpa_card a set wpa_num = (
select wpa_num from (
select wpa_id,wpa_num from wpa_card as of timestamp to_timestamp('20220610 12:00:00','yyyyMMdd hh24:mi:ss') where wpa_num > 1
) b where a.wpa_id = b.wpa_id
) where wpa_id in (
select wpa_id from wpa_card as of timestamp to_timestamp('20220610 12:00:00','yyyyMMdd hh24:mi:ss') where wpa_num > 1
)

MERGE关键字

MERGE关键字是一个神奇的DML关键字,它能将INSERT,UPDATE,DELETE等操作并为一句,根据与源表联接的结果,对目标表执行插入、更新或删除操作。

案例一:将查询结果更新到另一个表

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
-- 查询 已审批通过的在建工程转固报销单
-- 获取 转固单号 和 所包含的在建工程清单中的资产号
select listagg(t_code,'、')within group(order by t_code) t_code,a_code from (
select b.a_code,a.t_code from p_turn a,p_t_split b
where a.data_t='2' and a.audit = 3
and a.t_id = b.t_id(+)
) e group by a_code



-- (上边查询出来的数据)更新到 在建工程台账
merge into a_build c
using (
select listagg(t_code,'、')within group(order by t_code) t_code,a_code from (
select b.a_code,a.t_code from p_turn a,p_t_split b
where a.data_t='2' and a.audit = 3
and a.t_id = b.t_id(+)
) e group by a_code
) d
on (c.a_code = d.a_code)
when matched then update set c.t_code = d.t_code
where c.is_turn = 1;



-- 检查更新结果
select t_code from a_build where audit = 1 order by t_code;

案例二:如果存在数据则更新否则新增

1
2
3
4
#建议使用(Oracle 9以上版本):
MERGE INTO sp_store_ext A USING (select sp_store.*,{NEWKEYID} aa from sp_store where data_type='1' and house_name=? and sp_code=? and bj_type='1') B ON (A.store_id=B.store_id)
WHEN MATCHED THEN UPDATE SET A.in_date=?,A.store_month=?,A.device_name=?,A.device_code=?,A.abc_type=?,A.use_devnum=?,A.use_num=?,A.rep_pc=?,A.pc_num=?,A.month_num=?,A.pur_cycle=?,A.cb_memo=?,A.type=?,A.c_reason=?,A.status=?,A.xh_time=?,A.out_type=?,A.memo=?
WHEN NOT MATCHED THEN INSERT(A.ext_id,A.store_id,A.in_date,A.store_month,A.device_name,A.device_code,A.abc_type,A.use_devnum,A.use_num,A.rep_pc,A.pc_num,A.month_num,A.pur_cycle,A.cb_memo,A.type,A.c_reason,A.status,A.xh_time,A.out_type,A.memo) VALUES(B.store_id,B.store_id,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)

字符相关

长度

在select语句中,使用函数length(),获取数据(内容)长度。也可用于order by中。

1
2
3
4
5
6
7
8
9
10
11
select length(user_code) 账号长度,user_code 账号,user_name 用户名 from sys_user

-- 查询结果:
10 wangteng10 王腾
10 cuixiaoyu5 崔晓雨
10 lijinhui10 李金辉

-- 根据账号长度,从小到大(默认)排序
select length(user_code) 账号长度,user_code 账号,user_name 用户名 from sys_user order by length(user_code)
-- 根据账号长度,从大到小排序
select length(user_code) 账号长度,user_code 账号,user_name 用户名 from sys_user order by length(user_code) desc

去除空格

批量去除字段值空格

1
2
3
4
5
6
7
8
9
10
11
12
13
#去除左边空格
update tableName set colName=ltrim(colName);

#去除右边空格
update tableName set colName=rtrim(colName);

#去除两边空格
update tableName set colName=trim(colName);

#例子:
update sp_store_ext set ext_id=trim(ext_id);

#函数也可用于select查询语句中

截取字符串

  • 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
2
3
4
5
6
#大小写转换
#转换为小写 LOWER()
#转换为大写 UPPER()

#案例:
update spec_lowspare set import_degree = LOWER(import_degree) where lowspare_id = '1630'

拼接

同一行的记录,将不同字段的数据拼接在一起输出。可以使用连接运算符||函数实现。

不同行的记录,通过分组函数聚合后,将同一字段的数据拼接在一起。可以使用listagg函数、xmlagg函数实现,见下边的列转行部分。

连接运算符

||连接运算符

1
2
3
4
5
6
select '姓名:' || c.stuname || ', 课程:' || b.coursename || ', 成绩:' || a.score || '分数' as sxcj
from score a, course b, stuinfo c
where a.courseid = b.courseid
and a.stuid = c.stuid

#查询出来的数据,将会带上特定前缀(前边使用连接运算符拼接的姓名、课程等)

列转行

列转行,即多条记录转换为一条记录,使多行显示在一行。

  • listagg函数

    • 该函数的返回值是varchar类型,最大长度4000,超过4000的话,数据库查询会报错【ORA-01489:字符串连接的结果过长】
  • xmlagg函数

    • 该函数的返回值是clob类型
    • 在Java中需要用java.sql.Clob类,进行数据的接收与转换
  • case when then

原文链接:函数Listagg与xmlagg区别

listagg函数

原文链接:Oracle函数之LISTAGG

1
2
3
4
5
6
//用法:
LISTAGG(1,2) WITHIN GROUP( ORDER BY 3)
1:需要拼接的数据表中的字段
2:以什么字符分割,例如:','';'
3:排序字段。决定1中字段的拼接顺序
可使用在普通查询语句中、使用Group by分组函数的查询语句中,使用partition by分析函数的查询语句中,将指定的字段值拼接在一起(多行转一行)。

案例:

案例一

1
2
3
4
5
6
7
8
9
10
11
#查询仓库名
select house_name 仓库名 from sp_house where rownum <=3
仓库名
工具配件
二厂周转仓库
一厂周转仓库

#使用listagg函数,使多行显示在一行
select listagg(house_name,';')within group(order by house_code) 仓库名 from sp_house where rownum <=3
仓库名
工具配件;一厂周转仓库;二厂周转仓库

案例二

1
2
3
4
select a.user_name 姓名,a.user_code 账号,a.dept_name 部门,a.add_date 开通日期,listagg(a.role_name,';')within group(order by a.role_name) 角色
from
(select sys_user.user_name user_name,sys_user.user_code user_code,sys_dept.dept_name dept_name,sys_user.add_date add_date,sys_role.role_name role_name from sys_user,sys_dept,sys_user_role,sys_role where sys_user.user_id = sys_user_role.user_id(+) and sys_user_role.role_id = sys_role.role_id(+)and sys_user.dept_id = sys_dept.dept_id(+) and sys_user.is_novalid='0' and sys_role.role_name is not null) a
group by a.user_code,a.user_name,a.dept_name,a.add_date

查询结果:

xmlagg函数

用法:xmlagg(xmlparse(content 聚合的字段||’,’ wellformed) order by 分组的字段).getclobval() as 新的列名

案例:

1
2
3
4
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() 角色
from
(select sys_user.user_name user_name,sys_user.user_code user_code,sys_dept.dept_name dept_name,sys_user.add_date add_date,sys_role.role_name role_name from sys_user,sys_dept,sys_user_role,sys_role where sys_user.user_id = sys_user_role.user_id(+) and sys_user_role.role_id = sys_role.role_id(+)and sys_user.dept_id = sys_dept.dept_id(+) and sys_user.is_novalid='0' and sys_role.role_name is not null) a
group by a.user_code,a.user_name,a.dept_name,a.add_date

查询结果与上边listagg函数的案例二相同。

case when then

案例见这里

替换

见后边的正则表达式部分。

特殊字符

英文单引号'

  • 单引号'为特殊字符,在单引号包裹的字符中若有单引号需要对其进行转义,只需多写一个单引号即可,两个单引号代表一个单引号(转义)。
  • 使用运算符||进行拼接。例如:apple's => 'apple'||''''||'s',四个'代表加了单引号包裹的单引号。或'apple''s'

英文双横杠--

  • 使用运算符||进行拼接。例如:A--302 => 'A'||'-'||'-'||'302',四个'代表加了单引号包裹的单引号

英文分号;

  • 使用运算符||进行拼接。若数据为中文可考虑将分号替换为中文分号

like过滤

普通like

笔记摘自:CSDN

LIKE比较运算符:

  • 可以使用以下两个通配符%_,其中%代表零个或多个字符,_代表一个且只能是一个字符。
  • 如果要查询的数据中含有_%,需使用转义(escape)关键字
  • 适用于一个过滤条件
1
2
3
4
5
-- 示例:查询c_where字段中,包含 '101303%' 的数据。
-- 数据中包含%,%为通配符,需要使用转义关键字处理。
-- 数据中包含单引号',单引号'为特殊字符需要转义,写成两个单引号
select * from wf_u
where c_where like '%''101303\%''%' escape '\'

like函数

笔记摘自:字段like多个条件(or关系)简写

使用like过滤时有多个条件,简写如下:

1
2
3
4
5
6
7
8
select * from tablex where REGEXP_LIKE(字段名, '(匹配串1|匹配串2|...)') ;//全模糊匹配

select * from tablex where REGEXP_LIKE(字段名, '^(匹配串1|匹配串2|...)') ";//右模糊匹配(以xx开头)

select * from tablex where REGEXP_LIKE(字段名, '(匹配串1|匹配串2|...)$') ";//左模糊匹配

#案例:(以10、11等开头)
select distinct acc_name from asset_card where REGEXP_LIKE(type_code, '^(10|11|12|13|16|17|18)') and acc_name is not null

案例:

1
2
3
4
5
6
7
8
9
10
11
12
-- OT事业部 所有账号
select * from s_user where REGEXP_LIKE(d_id,
(
-- 查找所有 部门全称中包含“OT事业部”的部门,截取部门根ID(前4位,顶层部门的id),分组去除重复后拼接成 REGEXP_LIKE函数的参数,用于查找对应部门下的账号
select '^('||listagg(d_id,'|')within group(order by d_id)||')' from (
select d_id from (
select SUBSTR(d_id,04) d_id from s_dept where dept_all like '%OT事业部%' and is_disable = 0
) a group by d_id)
)
) and is_disable = 0
-- 过滤 没有添加指定的数据权限的账号
and not exists( select add_uid from s_user_funx where u_id = s_user.u_id and add_uid = 's_om_mb' group by add_uid having count (add_uid) = 3)

查看表/视图结构

1
2
SELECT DBMS_METADATA.GET_DDL('TABLE','表名 大写') FROM DUAL;
SELECT DBMS_METADATA.GET_DDL('VIEW','视图名 大写') FROM DUAL;

查看表/视图数量

1
2
3
4
-- 表数量
select count (*) from user_tables
-- 视图数量
select count (*) from user_views

查询带有某个字段的所有表

1
2
3
-- lower()函数,将大写转换为小写
-- 包含字段“dept_id”的表的数量
select LOWER(table_name) 表名,LOWER(column_name) 列名 from DBA_TAB_COLUMNS where COLUMN_NAME= 'DEPT_ID';

查询某个表

只知道表名中的几个字符,查找表

1
2
-- 查找表名中包含“SP_CAT”的表(大写)
select * from user_tables where table_name like '%SP_CAT%'

查看表数据大小

1
2
3
select segment_name, sum(bytes) / 1024 / 1024 from user_extents where segment_type='TABLE' and segment_name='SP_CATALOG' group by segment_name
-- 或者:
select owner,segment_name,sum(bytes) / 1024 / 1024 M from dba_extents u where u.segment_type='TABLE' and segment_name = 'SP_CATALOG' group by owner,segment_name

整合

查询表,并显示表数据大小

1
2
3
4
5
6
7
select a.last_analyzed 最近一次更新时间,a.table_name 表名,'0'||b.mysize||'M' 数据量,a.tablespace_name 表空间,b.owner 所有者 from user_tables a,(
select owner,segment_name,sum(bytes) / 1024 / 1024 mysize
from dba_extents u where u.segment_type='TABLE' group by owner,segment_name
) b
where a.table_name = b.segment_name(+)
and a.table_name like '%SP_CATALOG%'
order by a.last_analyzed desc

统计

常用函数:

  • AVG: 计算平均数

  • MAX: 获取最大值

  • MIN: 获取最小值

  • SUM: 计算总和

  • COUNT: 计算数据条数

group by

总结:

select子句后的任一非聚合函数字段都应来源于group by 分组语句后,否则语法会编译不通过。

可以把分出来的组理解为试图,所有的其他操作都基于这张视图。

当在一个SQL中同时使用where和group by和having子句时,其执行顺序为:where>group by>having。

同时where子句作用于表或者视图,having子句作用于组,having子句必须作用在group by之后。

摘自Oracle分组查询group by的用法及讲解

partition by

partition by用于给结果集分组,如果没有指定那么它把整个结果集作为一个分组。案例见上边的基于时间查询

小结:

group bypartition by 都有分组统计的功能,但是partition by并不具有group by的汇总功能。partition by统计的每一条记录都存在,而group by将所有的记录汇总成一条记录(类似于distinct EmpDepartment 去重)。partition by可以和聚合函数结合使用,同时具有其他高级功能。

case when then

case when then用于解决字符映射

1
2
3
4
5
6
7
8
9
10
11
12
1.简单Case函数写法(注意sex的位置)

select *,(CASE sex WHEN '1' THEN '男' WHEN '0' THEN '女' ELSE '保密' END) as sex_text
from user

2.Case搜索函数写法(注意sex的位置【推荐】)

select *,(CASE WHEN sex='1' THEN '男' WHEN sex='0' THEN '女' ELSE '保密' END) as sex_text
from user

#null判断
CASE WHEN 字段 is null THEN 0 ELSE 字段 END

参考这篇博客:select case when if 的一些用法

获取重复数据

笔记摘自:oracle查询重复数据方法

分组函数group by,结合having count(*) > 1查询表中重复的数据(指定单个字段)。

1
2
3
4
5
6
7
-- 通过 table_id,name,age 判断是否相同
-- having count (table_id) > 1,取有1条以上的分组数据即有重复的
select count(*) 数量,name,table_id,age from mytable2 group by table_id,name,age having count (table_id) > 1


-- having count(*)
-- 统计某一列的数据

案例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分组统计

  1. 查询周转库库存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
    14
    select 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,是为了测试时显示出物资名称
  2. 第一步的查询结果,每一行(条)记录为某周转库中某一物资的数量。以物资编码作为条件过滤(例如: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
    15
    select 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
  3. 第一步查询结果作为新表,通过物资编码关联表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
    31
    CREATE 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'

数据恢复

笔记摘自:三种方式恢复oracle数据库误删除的数据

恢复Delete

恢复少量数据

使用oracle提供的闪回方法进行数据恢复,适用于delete删除(少量数据)方式。

查看快照中数据(能查询出数据,才能恢复):

1
2
3
4
5
6
7
8
9
10
11
#as of timestamp - 应用Flashback Query查询过去的数据(基于时间的查询)

select * from 表名 as of timestamp to_timestamp('删除时间点','yyyy-mm-dd hh24:mi:ss')

例如:
#查看数据
select * from mytable
#误删
delete from mytable
#查看快照(可加where条件在后边)
select * from mytable as of timestamp to_timestamp('2021-12-29 15:27:00','yyyy-mm-dd hh24:mi:ss')

恢复数据:

将从快照中查询出来的数据插入原表中,注意要保证主键不重复。

1
2
3
4
insert into 表名 (select * from 表名 as of timestamp to_timestamp('删除时间点','yyyy-mm-dd hh24:mi:ss'));

例如:
insert into mytable (select * from mytable as of timestamp to_timestamp('2021-12-29 15:27:00','yyyy-mm-dd hh24:mi:ss'))

扩展:基于时间的查询,查看某一时间点后新增的记录

1
2
3
select * from mytable where my_id not in (
select my_id from mytable as of timestamp to_timestamp('2022/1/6 11:21:00','yyyy-MM-dd hh24:mi:ss')
)

恢复整张表

闪回整个表来恢复误删除的数据,但仅适用于表结构没有发生改变且用户有flash any table权限的情况下。

当需要使用flashback table功能时,需要先打开row mvoement的选项(启用表的行移动),否则使用该功能会报错。

1
2
3
4
5
6
7
8
9
10
11
12
#将row movement设置为enable
alter table 表名 enable row movement
#将表中数据闪回到某一时间点
flashback table 表名 to timestamp to_timestamp('删除时间点','yyyy-mm-dd hh24:mi:ss')

#例子:
#误删
delete from mytable
#enable
alter table mytable enable row movement
#恢复
flashback table mytable to timestamp to_timestamp('2021-12-29 15:29:00','yyyy-mm-dd hh24:mi:ss')

恢复Drop

因为oracle数据库在删除表时会将删除信息存放于某虚拟“回收站”中而非直接清空,再此种状态下数据库标记该表的数据库为“可以复写”,所以在该块未被重新使用前依然可以恢复数据。该方法多用于drop删除

首先需要查询user_table视图找到被删除的表:

1
2
3
4
#查看所有未被drop的表
select table_name,dropped from user_tables
#查看已被drop的表
select object_name,original_name,type,droptime from user_recyclebin

original_name为原表名,table_name和object_name就是回收站中的存放表名,如果明确原表的名称可以通过以下命令恢复:

1
2
#将表恢复到drop前
flashback table 原表名 to before drop

如果不知道原表名称可以直接按照回收站中的表名称将数据恢复,通过表数据确定是否是要恢复的表,最后对数据表进行重命名即可。

1
2
#将表恢复到新表(重新创建一个表)
flashback table "回收站中的表名(如:Bin$DSbdfd4rdfdfdfegdfsf==$0)" to before drop rename to 新表名

恢复数据库

利用oracle数据库的闪回功能可以将数据库恢复到过去某一状态,语法如下:

1
2
3
SQL>alter database flashback on
SQL>flashback database to scn SCNNO;
SQL>flashback database to timestamp to_timestamp('2007-2-12 12:00:00','yyyy-mm-dd hh24:mi:ss');

未测试过。上边的方法足够恢复数据使用。

扩展

永久性删除数据

1、采用truncate方式进行截断。(但不能进行数据回恢复了)

2、在drop时加上purge选项:drop table 表名 purge

该选项也可以通过删除recyclebin区域来永久性删除表 ,原始删除表drop table emp cascade constraints

purge table emp;

删除当前用户的回收站:

purge recyclebin;

删除全体用户在回收站的数据:

purge dba_recyclebin_frombyte.com

锁表与解锁

笔记摘自:Oracle锁表查询和解锁方法oracle锁表原因和如何解锁分享

锁表原因:

  1. 锁表的原理:数据库使用独占式封锁机制,当执行上面的语句时,对表进行锁住,直到发生commite 或者 回滚 或者退出数据库用户
  2. 主要的锁表原因有:
    • A程序执行了对 tableA 的 insert ,并还未 commite时,B程序也对tableA 进行insert 则此时会发生资源正忙的异常 就是锁表
    • 锁表常发生于并发而不是并行(并行时,一个线程操作数据库时,另一个线程是不能操作数据库的,cpu 和i/o 分配原则)
  3. 减少锁表的概率方法:
    • 减少insert 、update 、delete 语句执行 到 commite 之间的时间。具体点批量执行改为单个执行、优化sql自身的非执行速度
    • 如果异常对事务进行回滚

查看被锁表信息:

1
2
#object_name 被锁的表名
select sess.sid,sess.serial#, lo.oracle_username,lo.os_user_name, ao.object_name,lo.locked_mode from v$locked_object lo,dba_objects ao,v$session sess where ao.object_id=lo.object_id and lo.session_id=sess.sid;

杀掉锁表进程:

1
2
3
4
#后边参数分别为SID和SERIAL#
alter system kill session '135,45402';

alter system kill session 'SID,SERIAL#';

查看什么语句导致锁表的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
select l.session_id sid,
s.serial#,
l.locked_mode,
l.oracle_username,
s.user#,
l.os_user_name,
s.machine,
s.terminal,
a.sql_text,
a.action
from v$sqlarea a, v$session s, v$locked_object l
where l.session_id = s.sid
and s.prev_sql_addr = a.address
order by sid, s.serial#;

查看有几个表被锁:

1
select count(*) from v$locked_object;

not exists子查询

案例一:

用户表sys_user、用户角色表sys_user_role、角色表sys_role

查询 用户表中stafftype = ‘01’且没有角色“项目管理员”的 用户。

1
2
3
select * from sys_user
where sys_user.stafftype = '01'
and not exists (select 1 from sys_user_role,sys_role where sys_user.USER_ID = sys_user_role.USER_ID(+) and sys_user_role.ROLE_ID = sys_role.ROLE_ID and sys_role.role_name = '项目管理员')

案例二:

在插入数据前,查询表中是否已有再决定是否要执行插入操作。

若sys_user_data表中该用户已有dtype_data为1034的记录则不插入数据:

1
2
3
4
5
insert into sys_user_data (user_id,dtype_id,dtype_data,display,has_sub,add_date,add_userid)
select 'jxstar-351-1550','jxstar1','1034','MB事业部','1',sysdate,'deptid_now'
from dual
where not exists
(select * from sys_user_data where user_id = 'jxstar-351-1550' and dtype_id = 'jxstar1' and dtype_data = '1034');

正则表达式

笔记摘自:CSDN

Oracle中使用正则表达式:

  1. regexp_like,用于条件表达式,和 like 类似

  2. regexp_substr,用于截取符合正则表达式描述的字符子串

  3. regexp_instr,用于标定符合正则表达式的字符子串的开始位置

  4. regexp_replace,用于替换符合正则表达式的字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#案例:
#查看 factory_name字段 中间包含数字的记录

#regexp_like
select * from wpa_card
where REGEXP_LIKE(factory_name, '^.+[0-9]+.+$')

#regexp_substr
select * from wpa_card where
REGEXP_SUBSTR(factory_name, '^.+[0-9]+.+$') IS NOT NULL


#将 factory_name字段 中间包含数字2451002的记录 的factory_name字段值修改。将'2451002'更改为'订单物流部'。

#regexp_replace
update wpa_card set factory_name = regexp_replace(factory_name,'2451002','订单物流部') where wpa_attr = '10'
and rep_dept_name = '欧曼制造订单物流包装规划科'
and REGEXP_SUBSTR(factory_name, '^.+2451002.+$') IS NOT NULL

#查询数据,使用regexp_replace对查询出来的数据进行处理
select regexp_replace(factory_name,'2451002','订单物流部') as factory_name,wpa_code from wpa_card where wpa_code = 'SC016H-0001'

存储过程

笔记摘自:CSDN

存储过程:procedure

定义

定义简单存储过程:

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
31
32
33
34
35
36
37
38
39
40
41
42
-- 案例1
CREATE OR REPLACE PROCEDURE demo AS/IS
变量2 DATE;
变量3 NUMBER;
BEGIN
--要处理的业务逻辑
EXCEPTION --存储过程异常
END

-- 案例2
CREATE OR REPLACE PROCEDURE 存储过程名称(
s_no in varchar,
s_name out varchar,
s_age number) AS
total NUMBER := 0;
BEGIN
SELECT COUNT(1) INTO total FROM student s WHERE s.age=s_age;
dbms_output.put_line('符合该年龄的学生有'||total||'人');
EXCEPTION
WHEN too_many_rows THEN
DBMS_OUTPUT.PUT_LINE('返回值多于1行');
END

-- 案例3
CREATE OR REPLACE PROCEDURE updateContractDept(
p_code in varchar,
p_dept_name in varchar,
p_dept_id in varchar) AS
dept_name_old varchar(50);
dept_id_old varchar(50);
dept_name_now varchar(50);
dept_id_now varchar(50);
BEGIN
SELECT dept_name,dept_id INTO dept_name_old,dept_id_old FROM project_contract WHERE contract_code = p_code;
update project_contract set dept_name = p_dept_name,dept_id = p_dept_id where contract_code = p_code
and exists ( select 1 from sys_dept where dept_id = p_dept_id );
SELECT dept_name,dept_id INTO dept_name_now,dept_id_now FROM project_contract WHERE contract_code = p_code;
dbms_output.put_line('合同【'||p_code||'】的实施部门已由【'||dept_name_old||dept_id_old||'】修改为【'||dept_name_now||dept_id_now||'】!');
EXCEPTION
WHEN others THEN
dbms_output.put_line('修改失败!');
END;

定义稍复杂一点的存储过程:

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
31
32
-- 更新部门(名称、id)
create or replace procedure updateDept(
-- 接收的参数
p_table_name in varchar,
p_key in varchar,
p_value in varchar,
p_dept_name in varchar,
p_dept_id in varchar)
AS
-- 变量
dept_name_old varchar(50);
dept_id_old varchar(50);
dept_name_now varchar(50);
dept_id_now varchar(50);
sql_getdept varchar2(2000);
sql_update varchar2(2000);
begin
-- 拼接sql
sql_getdept := 'SELECT dept_name,dept_id FROM '||p_table_name||' WHERE '||p_key||' = '''||p_value||'''';
sql_update := 'update '||p_table_name||' set dept_name = '''||p_dept_name||''',dept_id = '''||p_dept_id||''' where '||p_key||' = '''||p_value||''' and exists ( select 1 from sys_dept where dept_id = '''||p_dept_id||''' )';
-- 执行前边拼接好的sql,并将查询结果保存在变量中
execute immediate sql_getdept into dept_name_old,dept_id_old;
execute immediate sql_update;
execute immediate sql_getdept into dept_name_now,dept_id_now;
-- 输出语句
dbms_output.put_line('部门已由【'||dept_name_old||dept_id_old||'】修改为【'||dept_name_now||dept_id_now||'】!');
-- 异常处理
EXCEPTION
-- 使用others,只要出现异常就会打印下边的语句
WHEN others THEN
dbms_output.put_line('修改失败!');
end;

调用

1
2
#执行存储过程
call 存储过程名()

删除

1
2
#删除存储过程
drop procedure 存储过程名;
若图片不能正常显示,请在浏览器中打开

欢迎关注我的其它发布渠道