前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Oracle DBA的SQL编写技能提升宝典(含SQL资源)

Oracle DBA的SQL编写技能提升宝典(含SQL资源)

作者头像
数据和云
发布2021-10-13 14:45:11
1K0
发布2021-10-13 14:45:11
举报
文章被收录于专栏:数据和云数据和云

01

一、运维场景SQL实例

案例一:创建表空间

背景:要迁移数据库,需要创建与源库相同的表空间,大小与源库相同。由于个别表空间较大,手工添加可能需要写很多的脚本,于是同事通过PL/SQL解决了问题。

但实际上通过一条SQL就可以搞定,步骤👇

创建表空间——添加数据文件,直到与源库大小相同

  1. 查询源表空间的大小;
  2. 生成文件列表<最大文件数为1024>;
  3. 进行关联。

SQL如下:

代码语言:javascript
复制
col sqltext for a999
with t as
(select tablespace_name tsname, round(sum(bytes) / 1024 / 1024 / 1024) gb  
from dba_data_files  group by tablespace_name),
t2 as
(select rownum n from dual
        connect by rownum < 1024)
select decode(b.n, 1,'create tablespace','alter tablespace')||
       a.tsname || ' datafile''+DATADG''size 30G;'sqltxt
  from t a, t2 b
where30 * b.n < a.gb
orderby a.tsname, b.n;
案例二:巡检-异常检测-周期数据

数据库主机CPU一般每天随着上下班时间进行规律性波动。在这种数据中,怎么找出CPU使用率异常的主机、时间点?

1、从一堆数据库中找出异常的数据库节点

2、找出波动区域后,针对异常节点Excel画图,以观察其与正常负载的区别

3、异常主机查询-主要SQL介绍

a. 以天、小时为单位对数据分组。

b. 用分析函数取平均值分析标准方差,以更加精准定位问题主机。

代码语言:javascript
复制
with t as
 (select to_char(exectime, 'yyyy-mm-dd') d,
         to_char(exectime, 'hh24') h,
         hostname,
         round(avg(100 - id)) cpu
    from sys_vmstat
   where exectime between trunc(sysdate - 7, 'dd') and trunc(sysdate, 'dd')
     and (hostname like 'yyyy%' or hostname like 'zzzz%')
   group by to_char(exectime, 'yyyy-mm-dd'),
            to_char(exectime, 'hh24'),
            hostname),
t2 as
 (select d,
         h,
         hostname,
         cpu,
         round(avg(cpu) over(partition by hostname, h)) avg_cpu,
         round(STDDEV(cpu) over(partition by hostname, h)) cpu_stddev
    from t)
select *
  from t2
 where cpu_stddev < 15
   and cpu - avg_cpu > 20
 order by cpu - avg_cpu desc;

c. 生成7天对比图,使用pivot函数将每天每小时的SQL使用率作图。

代码语言:javascript
复制
with t as (select to_char(exectime,'yyyy-mm-dd')day,to_char(exectime,'hh24') hour,hostname,round(avg(100-id)) cpu from gm.sys_vmstat  
where exectime between trunc(sysdate-7,'dd') and  trunc(sysdate,'dd') and hostname='testb2'
group by to_char(exectime,'yyyy-mm-dd'),to_char(exectime,'hh24') ,hostname)
SELECT day,
       "00-01_ ",
       "01-02_ ",
       "02-03_ ",
       "03-04_ ",
       "04-05_ ",
       "05-06_ ",
       "06-07_ ",
       "07-08_ ",
       "08-09_ ",
       "09-10_ ",
       "10-11_ ",
       "11-12_ ",
       "12-13_ ",
       "13-14_ ",
       "14-15_ ",
       "15-16_ ",
       "16-17_ ",
       "17-18_ ",
       "18-19_ ",
       "19-20_ ",
       "20-21_ ",
       "21-22_ ",
       "22-23_ ",
       "23-24_ "
 From  t pivot(sum(CPU) as " " for hour in('00' AS "00-01",
                                         '01' AS "01-02",
                                         '02' AS "02-03",
                                         '03' AS "03-04",
                                         '04' AS "04-05",
                                         '05' AS "05-06",
                                         '06' AS "06-07",
                                         '07' AS "07-08",
                                         '08' AS "08-09",
                                         '09' AS "09-10",
                                         '10' AS "10-11",
                                         '11' AS "11-12",
                                         '12' AS "12-13",
                                         '13' AS "13-14",
                                         '14' AS "14-15",
                                         '15' AS "15-16",
                                         '16' AS "16-17",
                                         '17' AS "17-18",
                                         '18' AS "18-19",
                                         '19' AS "19-20",
                                         '20' AS "20-21",
                                         '21' AS "21-22",
                                         '22' AS "22-23",
                                         '23' AS "23-24"))

案例三:巡检-异常检测-异常波动

平时维护数据库较多时,若想通过人工找出某一数据库在何时间发生过较大的负载波动,会比较麻烦,通过以下SQL筛查的方式会相对高效。

具体查询SQL可从后文“SQL资源下载”中的资源包下载。

1、通过函数将前后几分钟的负载变化选出

2、通过发生问题的时间点、实例采用LISTAGG生成负载变化图,通过excel生成直观曲线图

最后制作成的Excle表格如下:

案例四:巡检-表空间分析

通过分析表空间的变化,可以了解表空间有无异常增长或变化明显的情况,以合理规划表空间。

1、存储空间规划

如以下表空间每天增长、清理,有少量净增长,表空间预留多大才能使用90天?

计算公式:每天净增长*预留天数+每天最大使用量+最小保留

净增长的计算方式:

2、等待链

在分析性能问题或堵塞时需要对等待链进行分析。

代码语言:javascript
复制
set lines 1000
col wait_chain format a160
with t as
 (select * from gv$session),
t2 as
 (select level lv,
         sid,
         serial#,
         program,
         event,
         connect_by_iscycle iscycle,
         connect_by_isleaf leaf,
         LEVEL,
         SYS_CONNECT_BY_PATH('['||program||']'||'('||                
           to_char(inst_id)||'-'||nvl(event,state)||')',
           '->') wait_chain
   from t
 connect by NOCYCLE prior blocking_session= sid
          and  prior blocking_instance= inst_id
   start with state='WAITING')
select wait_chain,count(*),max(iscycle)iscycle 
  from t2 
 where leaf=1
   AND LV>1 
 group by wait_chain
 order by count(*) desc;

不同的场景则需对SQL进行调整。

(1)使用gv%session函数

(2)使用ISCYCLE

右侧可看到堵塞者为SQL*Net message from client,为空闲等待事件,即活堵塞处为非活动状态,在执行SQL后未及时提交事务,导致堵塞。

3、ASH分析

进行ASH分析,分析等待事件的变化,可以使用event或sql_id执行。

(1)event

代码语言:javascript
复制
----by event
break on etime
with t as (select to_char(sample_time,'hh24:mi')etime,nvl(event,'ONCPU')event,round(count(*)/60,2) cnt,row_number()over(partition by to_char(sample_time,'hh24:mi')order by count(*) desc) rn from v$active_session_history
where sample_time>sysdate-30/1440
group by to_char(sample_time,'hh24:mi') ,nvl(event,'ONCPU'))
select * from t where rn<=10 order by etime,cnt desc;

(2)sql_id

可以通过函数,看到百分比以及某一时间点的使用次数。

代码语言:javascript
复制
---by sqlid
break on etime
with t as (select to_char(sample_time,'hh24:mi')etime,nvl(sql_id,TOP_LEVEL_CALL_NAME) sql_id,round(count(*)/60,2) cnt,round(ratio_to_report(count(*))over(partition by to_char(sample_time,'hh24:mi'))*100,2) pct, max(SQL_EXEC_ID)-min(SQL_EXEC_ID)+1 execs,row_number()over(partition by to_char(sample_time,'hh24:mi')order by count(*) desc) rn from v$active_session_history
where sample_time>sysdate-30/1440
group by to_char(sample_time,'hh24:mi') ,nvl(sql_id,TOP_LEVEL_CALL_NAME))
select * from t where rn<=10 order by etime,cnt desc;

4、SQLPLUS 中的性能监控

可以使用 PL/SQL PIPE ROW 特性进行实时监控某些指标,函数中指标可配置,用逗号隔开即可。

主要SQL如下:

(具体查询SQL可从后文“SQL资源下载”中的资源包下载。)

代码语言:javascript
复制
dbms_lock.sleep(interval_sec);
PIPE ROW ('------------------------------------');
for r in (select /*+use_hash(s) leading(l,s)*/s.name,s.value,sysdate etime from table(dbmt.split(stat_str))l, v$sysstat s where l.column_value=s.name)
loop
v_interval_sec:=(r.etime-v_date)*24*3600;
ret_str:=to_char(r.etime,'hh24:mi:ss')||' '||rpad(r.name||'/s ',30,'-')||' '|| round((r.value-stat1(r.name))/v_interval_sec,2);
PIPE ROW (ret_str);
stat1(r.name):=r.value;
v_date_new:=r.etime;
end loop;
/

二、

常用函数介绍、示例

1、分析函数

用来支持进行OLAP(联机分析处理),提供强大的分析功能。

分析函数语法及释义:

代码语言:javascript
复制
FUNCTION\_NAME(<argument>,<argument>..…)

OVER(<Partition-Clause><Order-by-Clause><Windowing Clause>)

(1)FUNCTION子句

Oracle提供了很多分析函数,按功能分5类:等级(ranking)函数,开窗(windowing)函数,制表(reporting)函数,LAG/LEAD函数,线性的衰减函数。

(2)PARTITION子句

按照表达式分区(就是分组),如果省略了分区子句,则全部的结果集被看作是一个单一的组。

可根据实际情况进行调整,是否需要依不同条件、值进行分组。

(3)ORDER BY子句

分析函数中ORDER BY的存在将添加一个默认的开窗子句,这意味着计算中所使用的行的集合是当前分区中当前行和前面所有行,没有ORDER BY时,默认的窗口是全部的分区。根据实际情况选择是否使用。

(4)WINDOWING子句

用于定义分析函数将在其上操作的行的集合。

Windowing子句给出了一个定义变化或固定的数据窗口的方法,分析函数将对这些数据进行操作,默认的窗口是一个固定的窗口,仅仅在一组的第一行开始,一直继续到当前行,要使用窗口,必须使用ORDER BY子句,根据2个标准可以建立窗口:数据值的范围(RANGE)或与当前行的行偏移量(ROWS)。

示例:

  • RANGE逻辑窗口

针对图中ID列的值作运算,RANGE_SUM列为逻辑窗口,意为当前行的值-1到当前行+2的窗口中所包含的值求和。

  • ROWS物理窗口

针对图中ID列的值作运算,ROWS_SUM列为物理窗口,意为当前行的前一行+当前行+后两行的值求和。

2、常用分析函数

(1)汇总类

  • Sum
  • Avg
  • Count
  • Max/min
  • Ratio_to_report

在通过ASH分析性能时,在进行分组后会计算活动会话的百分比,即可用此函数。

代码语言:javascript
复制
with t as
 (select to_char(sample_time,' hh24: mi') etime,
         nv1(sql_id, TOP_LEVEL_CALL NAME) sql_id,
         round(count(*)/60,2) cnt,
         round(ratio_to report(count(*)).
               over(partition by to_char(sample_time, 'hh24:mi'))*100,
               2) pct,
         max(SQL_EXEC_ID)-min(SQL_EXEC_ID)+1 execs,
         row_number() over(partition by to char(sample_time,' hh24: mi') order by count(*) desc) rn
    from vSactive_session history
   where sample_time>sysdate-30/1440
   group by to_char(sample_time,' hh24: mi'),
            nvl(sql id, TOP LEVELCALLNAME))
select * from t where rn<=10 order by ctime, cnt desc;
  • LISTAGG(多个值平均到一起)

将一个分组中的多个值合成一行。

示例:

使用listagg函数后,结果如下:

注意:超长溢出处理方式(最长4000字节)

a. ON OVERFLOW ERROR

b. ON OVERFLOW TRUNCATE

(2)排行类

  • Row_number
  • Rank

取每个用户下最多的两类对象

代码语言:javascript
复制
select*
 from(select owner,
        object_type,
        cnt,
        rank()over(partition by owner order by cnt desc)rank
      from(select owner,object_type,count(*)cnt
          from dbmt.db_objects
         group by owner,object_type)t)
where rank<
  • Dense_rank

注意

a. Row number分配一个唯一的行编号

b. Rank排名可能不是连续的数字

c. Dense_rank排名是连续的数字

(3)其他

  • LAG , LEAD

取当前行的前一/几行(LAG)或后一/几行(LEAD)中指定值

SQL与示例:

代码语言:javascript
复制
with t as
 (select rownum r from dual connect by level<10)
select r,
1ag(r)over(order by r)lagr,
lead(r)over(order by r)lead_r
  from t;

常用:分析AWR数据,用当前的值-上一行值即可计算增量,示例SQL如下:

代码语言:javascript
复制
select st. instance_number,
    st. snap_id,
    to_char(SN. BEGIN_INTERVAL_TIME,' yyyy-mm-dd hh24: mi") BEGIN_INTERVAL_TIME,
    (value-(lag(value)
    over(partition by st. instance_number order by st. snap_id))) value
 from dba_hist_sysstat st, DBA_HIST_SNAPSHOT sn
where st. INSTANCE_NUMBER=SN. INSTANCE_NUMBER
 and st. SNAP_ID=SN. SNAP_ID
 and sn. begin_interval_time> to_date("2021-03-01', yyyy-mm-dd")
 and sn. instance_number=1
 and stat_name=' gc cr blocks received'
order by st. instance_number, st. snap_id;
  • GREATEST , LEAST(取最大值、最小值)

常用于分析SQL历史性能:

代码语言:javascript
复制
select ss.plan hashvalue phv,
代码语言:javascript
复制
    to_char(s.begin_interval_time,'mm-dd HH24:MI")snap_time,
    ss.instance_number,
    ss.executions delta execs,
    round(ss.rows processed delta/greatest(ss.executions delta,1),2)rows per exec,
    round(ss.buffer gets delta/greatest(ss.executions delta,1))lio_per_exec,
    round(ss.disk_reads delta/greatest(ss.executions delta,1))pio_per_exec,
    round((ss.cpu time delta/1e3)/greatest(ss.executions delta,1),2)cpu_per_exec,
    round((ss.elapsed_time_delta/1e3)/greatest(ss.executions_delta,1),2)ela_per_exec
from dba_hist_snapshot s,
   dba hist_sqlstat ss
where ss.dbid=s.dbid
and ss.instance_number=s.instance_number
and ss.snap_id=s.snap_idand ss.sql_id='&v_sqlid'
and ss.executions_delta>0
and s.begin_interval_time>=sysdate-&v_days
order by ss.plan_hash_value,s.snap_id;

示例:找出7列相同或不同的记录:

SQL如下:

代码语言:javascript
复制
SELECT * FROM dbmt. ogg tables count2 WHERE GREATEST(ACNT, BCNT, CCNT, DCNT, ECNT, FCNT, GCNT)<>LEAST(ACNT, BCNT, CCNT, DCNT, ECNT, FCNT, GCNT)
  • FIRST_VALUE , LAST_VALUE

取分组中的第一个(FIRST_VALUE)、最后一个值(LAST_VALUE)

代码语言:javascript
复制
with t as(selectsubstr(time,1,5)d,
代码语言:javascript
复制
       time
       first_value(mb) gver(partition by substr(time,1,5) order by time)
begin_mb,
      mb,
      min(mb) over(partition by substr(time,1,5)) min_mb,
      max(mb) over(partition by substr(time,1,5)) max_mb
  from dbmt. tmp tbs used)
select distinct d, begin mb, min_mb, max_mb from t order by d
  • NVL ,NVL2
    • NVL(EXP,返回值1)当EXP值为null时返回值1,不为null时返回本身
    • NVL2(EXP,返回值1,返回值2)当exp的值为null时返回值1,不为null时返回值2
代码语言:javascript
复制
nvl(NULL,'N')==N
nvl'A','')==A
nv12(NULL,'A','B')==B
nv12('c','A','B')==A
  • LNNVL

LNNVL当条件的一个或两个操作数可能为空时,LNNVL提供了一种简明的方法来计算条件。它接受一个条件作为参数,如果条件为假或未知则返回TRUE,如果条件为真则返回FALSE。LNNVL可以在任何标量表达式可能出现的地方使用。

end_date is null or end_date>sysdate

可改为

Innvl(end_date<=sysdate);

  • DECODE(IF ELSE 分支判断)

DECODE(EXP,条件1,返回值1,条件2,返回值2,…,默认值)

一般用于行转列。示例:

代码语言:javascript
复制
select owner,
sum(decode(object type,' TABLE',1,0)) table cnt,
sum(decode(ob. ject type,' INDEX',1,0)) index cnt
from dbmt. db objects where object type in (' TABLE',' INDEX')
group by owner
  • LPAD , RPAD

LPAD(COLUMN,参数1,参数2)

LPAD指在column列的左边填充指定长度的指定字符串,RPAD指在右边填充。一般用于格式化数据。

示例:

代码语言:javascript
复制
SQL> select
rownum,LPAD(to_char(rownum),10,'0'),RPAD(to_char(rownum),10,'0') from dual
connect by rownum<4;
    ROWNUM LPAD(TO_CHAR(ROWNUM) RPAD(TO_CHAR(ROWNUM)
---------- -------------------- --------------------
         1 0000000001           1000000000
         2 0000000002           2000000000
         3 0000000003           3000000000
  • LTRIM , RTRIM

去掉一个字符串中左边(LTRIM)或右边(RTRIM)的字符

LTRIM(char [,set ])

RTRIM(char [,set ])

char参数为字符串,set为需要去掉的字符,若不指定具体字符默认为去掉空字符。

  • 一些正则表达式:REGEXP_LIKE、REPLACE、SUBSTR

可以指定参数

  • match_param
  • i 指定不区分大小写的匹配。
  • c 指定区分大小写的匹配。
  • n 允许句点.匹配换行符,省略则与换行符不匹配。
  • 'm’ 将源字符串视为多行。
  • Oracle将 ^ 和 $ 分别解释为源字符串中任何位置的任何行的开始和结束,而不仅仅是整个源字符串的开始或结束。如果省略此参数,则Oracle将源字符串视为一行
  • ORA_HASH计算HASH值

select object_id,

ora_hash(ownerllobject_name)hashval from dba obajects

  • Connect by

select * from table [start with condition1]

connect by [nocycle]

[prior]id=parentid==

1)[start with condition1]递归开始的条件,第一层。

2)connect by [prior] id=parented 递归条件。

3)[prior] id为当前层,parented为递归查询列,下次递归SQL类似select*from table where parented=id(当前层)。

示例:

代码语言:javascript
复制
set lines 400 col txt for a100 set tab off
with t as
(select*from vSsql_plan
        where sql_id="7fybj6y7ug6q2'
         AND CHILD_NUMBER=0).
select id,
    parent_id,
    LEVEL,
    lpad("", level*2-1,") || operation ||'' || options ||'' ||
    object_owner || decode(object_name, null,",.") || object_name txt
 from t
start with id=0
Connect by parent_id= prior id;
Select * from t where parent_id=0
Selct * from t where parent_id=1

Connect by 可用的函数、伪列

1)SYS_CONNECT_BY_PATH(column,char)层级路径。

2)CONNECT_BY_ISLEAF是否为页子节点。

3)LEVEL当前层级,始于1。

4)CONNECT_BY_ISCYCLE是否产生死循环,只有制定NOCYCLE时才能使用该伪列。

三、

常用SQL技巧

1、生成数据

(1)递归生成数字列表

代码语言:javascript
复制
select rownum rn from dual
       connect by rownum<=10;

rownum<=10;

改变这个条件,可生成不同数量的数字列表。

如下想生成偶数列表呢?

(2)生成日期列表,通过生成数字列表的方式扩展

代码语言:javascript
复制
select to_date('2017-01-01','yyyy-mm-dd')+rownum-1
date_day,
add_months(to_date('2017-01-01','yyyy-mm-dd'),rownum-1)
date_month from dual
       connect by rownum<=10;

根据不同需要,可以天列表,月列表。

(3)生成随机数据(使用包DBMS_RANDOM)

代码语言:javascript
复制
select rownum rn,dbms_random.string('a',10)
random_str,abs(mod(dbms_random.random(),100)) random_num
from dual
       connect by rownum<=10;
  • dbms_random.string(‘a’,10)

改变参数可限制生成的字符串长度

  • mod(dbms_random.random(),100)

改变参数可限制生成的数字范围

参数注意

  • 小数(0 ~ 1)

select dbms_random.value from dual ;

  • 指定范围内的小数 ( 0 ~ 100 )

select dbms_random.value(0,100) from dual ;

  • 指定范围内的整数 ( 0 ~ 100 )

trunc(dbms_random.value(0,100)) from dual ;

  • 随机字符串

select dbms_random.string(‘x’, 3) from dual ;

2、统计分析

使用case when 行转列

(与DECODE类似,但DECODE只能是等值查选)

如下面统计每个用户下,2017年以前创建的对象有多少,2017年及以后创建的对象有多少?

代码语言:javascript
复制
select owner,
sum(case when created'2017','yyyy') then 1 else 0end) "2017年以前",
sum(case when created>=to_date('2017','yyyy') then 1 else 0end) "2017年及以后"
from dbmt.db_objects where object_type in ('TABLE','INDEX')
group by owner
3、SQL技巧

查找连续值

可以查找出序列中的连续值或中断位置。

代码语言:javascript
复制
selectmin(id), max(id)
代码语言:javascript
复制
  from (select id,id - rownum rn
          from (select id from dbmt.list1
order by id))
 group by rn
having count(rn) > 1
 order by min(id);
 
 
MIN(ID) MAX(ID)
---------- ----------
         1          4  //1-4连续
         6          9  //6-9连续
        11         21  //11-21连续
        24         49  //24-49连续
4、XML TABLE

可通过函数提取分支数据。

代码语言:javascript
复制
with t as (select xmltype('value1value2') xmlvalfrom dual)
select i from t a,
            xmltable('/a/i' passing xmlval columns i path '/i')


老师在直播中还讲解了一些关于null的注意事项,感兴趣的朋友点击查看回放视频、PPT继续学习。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-10-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据和云 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 案例一:创建表空间
  • 案例二:巡检-异常检测-周期数据
  • 案例四:巡检-表空间分析
  • 1、分析函数
  • 2、常用分析函数
  • 1、生成数据
  • 2、统计分析
  • 3、SQL技巧
  • 4、XML TABLE
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档