Oracle 函数高级查询


目录
  • oracle高级查询
    • Oracle SQL获取每个分组中日期最新的一条数据
    • 求平均值(为0的参数不均摊)
    • 字符串清除前面的0
    • 判断字符串串是否包含某个字符串
    • switch 判断
    • oracle不足位数补零
    • 根据上级查询下级
    • 行转列
    • 列转行
    • 删除表中重复数据,重复数据是根据单个字段(Name)来判断,只留有rowid最小的记录
    • 查询第几次出现,也可以删除重复字
    • 判断是否为数字
    • 数据存在修改,不存在插入

oracle高级查询

Oracle SQL获取每个分组中日期最新的一条数据

select rn,a,b,c
  from (select ROW_NUMBER() OVER(PARTITION BY a ORDER BY b DESC) rn,a,b,c
              from table)

where rn = 1

求平均值(为0的参数不均摊)
select

SUBSTR(PERIOD,1,4) 年份,

SUBSTR(PERIOD,5,2)||'月' 期间,

'ORG_'||code 组织,

'不分人员'人员,

'DEPT1_'|| u9_biz_dept 部门,

round(sum_p/sum_month,6)

from (

select a.PERIOD,mud.code ,md.u9_biz_dept,

count(1) 人数,

SUM (count(1)) OVER (PARTITION BY SUBSTR (PERIOD, 1, 4),md.u9_biz_dept order by PERIOD) sum_p,

row_number() OVER (PARTITION BY SUBSTR (PERIOD, 1, 4),md.u9_biz_dept order by PERIOD) sum_month

from dw.DW_EHR_EMPLOYEE_BASE a left join V_nc_to_u9_dept md on md.nc_code=a.DEPTCODE

 left join ods.md_u9_dept mud on md.u9_pk_org=mud.pk_dept

where SUBSTR(a.PERIOD,1,4)>='2018' 

group by a.PERIOD,mud.code  ,md.u9_biz_dept

order by PERIOD

)


字符串清除前面的0
    LTRIM('00090','0')

判断字符串串是否包含某个字符串
instrcontains(carnumber, '粤')
CONTAINS( address, '"HEIBEI province"' )
instr(carnumber, '粤') > 0
dd.carnumber like '赣%'


switch 判断
decode(条件,值1,返回值1,值2,返回值2,...值n,返回值n,缺省值)

该函数的含义如下:
IF 条件=值1 THEN
    RETURN(翻译值1)
ELSIF 条件=值2 THEN
    RETURN(翻译值2)
    ......
ELSIF 条件=值n THEN
    RETURN(翻译值n)
ELSE
    RETURN(缺省值)
END IF


decode(字段或字段的运算,值1,值2,值3)


       这个函数运行的结果是,当字段或字段的运算的值等于值1时,该函数返回值2,否则返回值3
当然值1,值2,值3也可以是表达式,这个函数使得某些sql语句简单了许多


使用方法:


1、比较大小
select decode(sign(变量1-变量2),-1,变量1,变量2) from dual; --取较小值
sign()函数根据某个值是0、正数还是负数,分别返回0、1、-1
例如:
变量1=10,变量2=20
则sign(变量1-变量2)返回-1,decode解码结果为“变量1”,达到了取较小值的目的。


oracle不足位数补零
select rpad('AAA',5,'0') from dual; 这样就可以了
【注意】
1.'AAA'为待补字符;5表示补齐后的总字符长度;0表示不足时补什么字符
2.rpad是右侧补0,左侧补0可用lpad


根据上级查询下级
select
        *
    from
        fht_enterprise  start with id = 1218 connect
    by
        prior id =  parentid
start with 子句:遍历起始条件,有个小技巧,如果要查父结点,这里可以用子结点的列,反之亦然。
connect by 子句:连接条件。关键词prior,prior跟父节点列parentid放在一起,就是往父结点方向遍历;prior跟子结点列subid放在一起,则往叶子结点方向遍历,     parentid、subid两列谁放在“=”前都无所谓,关键是prior跟谁在一起。
order by 子句:排序,不用多说。

数状图上层级汇总下层级数据
with t as (    
select a.pk_dept,a.dept_type,SYS_CONNECT_BY_PATH(a.pk_dept,'/')||'/' paths from ods.md_u9_dept a
start with pk_dept = '~' connect
by prior pk_dept =  pk_fatherdept)
select c.*,
(select sum(dept_type) from t where  t.paths like c.paths||'%' )  sum_value
from t c


根据时间累加或累减值

select tt.item_no,
       tt.period,
       cmu-nvl(lag(cmu,1)over(partition by tt.item_no,substr(tt.period,1,4) order by tt.period),0) --上一月-这个月值
from tmp_cmu tt
order by 1,2


select a.PERIOD,corg_code,
   count(1) 人数,
   SUM (count(1)) OVER (PARTITION BY SUBSTR (PERIOD, 1, 4) order by PERIOD) sum_month --累计所有月份值,
   count(1) OVER (PARTITION BY SUBSTR (PERIOD, 1, 4) order by PERIOD) sum_month1
from dw.DW_EHR_EMPLOYEE_BASE a
where SUBSTR(a.PERIOD,1,4)>='2018' and corg_code='800'
group by a.PERIOD,corg_code order by PERIOD


行转列
with temp as(
select a.seccode,a.secname,a.F006N, a.F007N, a.F008N,  a.F009N from CI_DATAS_P_STOCK2303 a where  a.enddate='2018-12-31'
)
select seccode,secname,name,title from
temp
unpivot
(name for title in (F006N, F007N, F008N, F009N))t
列转行
SELECT * FROM [StudentScores] /*数据源*/
AS P
PIVOT
(
    SUM(Score/*行转列后 列的值*/) FOR
    p.Subject/*需要行转列的列*/ IN ([语文],[数学],[英语],[生物]/*列的值*/)
) T

例:
SELECT t.* FROM ( select sdata,dim_s,dim_y||dim_m a  from C1_OUTPUT_DATAS_FI01 where stype ='财务报表与指标')
PIVOT (sum(sdata) m1 ,max(a) m2 FOR dim_s IN ('实际' as 实际,'同比增长率' as 同比) )t  
删除表中重复数据,重复数据是根据单个字段(Name)来判断,只留有rowid最小的记录
delete from OA_ADDRESS_BOOK where (Name) in
(select Name from OA_ADDRESS_BOOK group by Name having count(Name) >1)
and rowid not in (select min(rowid) from OA_ADDRESS_BOOK group by Name having count(Name)>1)
查询第几次出现,也可以删除重复字
select * from (
  select  
  ROW_NUMBER() OVER(PARTITION BY  bp.code ORDER BY  hpo.joinsysdate DESC) rn,
  
to_char( to_date(hpo.joinsysdate,'yyyy-MM-dd  HH24:mi:ss'),'yyyy') 合并,
  'PEO'||to_char(to_number(bp.code)) 元素,
  bp.name||'_'||to_char(to_number(bp.code)) 中文名,
  hpo.endflag 失效
from ods.bd_psndoc bp
left join ods.hi_psnorg hpo on bp.pk_psndoc =  hpo.pk_psndoc
left join ods.hi_psnjob hpj on hpo.pk_psnorg =  hpj.pk_psnorg
  and hpj.lastflag = 'Y' and hpj.ismainjob = 'Y'
WHERE hpo.indocflag = 'Y'
   AND hpo.psntype = 0
   AND bp.code != '15421'   
order by hpo.joinsysdate desc  
) where  rn = 1
判断是否为数字
查询是否为数字:select * from table where regexp_like(column,'^[0-9]+[0-9]$');
查询非数值类型:regexp_replace(字段,'^[-\+]?\d+(\.\d+)?$','')
正则查询以字段开头:regexp_like(NATACC_CODE4,  '^(6001|6061|6401|1801|2201|2241|2203|2202)')

create or replace function Isnumber(P_column in varchar2) return number
is
l_t number;
begin
  l_t := to_number(P_column) ;
     return 1;
  exception when others then
     return 0;
end;


数据存在修改,不存在插入
merge法
--适合与两张表的关联查询
MERGE INTO table_name alias1   
USING (table|view|sub_query) alias2  
ON (join condition)   
WHEN MATCHED THEN   
    UPDATE table_name SET col1 = col_val1
WHEN NOT MATCHED THEN   
    INSERT (column_list) VALUES (column_values);
--例:
merge into Account t1  
using (select '3' AccountID,'肖文博' AccountName from dual) t2  
on (t1.AccountID = t2.AccountID)  
when matched then  
     update set t1.AccountName = t2.AccountName
when not matched then  
     insert values (t2.AccountID, t2.AccountName);  
commit;

--
no_data_found法
先查找要插入的记录是否存在,存在则修改,不存在则插入。用于防止插入冗余数据。
declare t_cols number;
begin
select AccountName into t_cols from account where AccountID = '8';
exception
when no_data_found then begin
   --dbms_output.put_line('添加');
   insert into account(AccountID,AccountName) values('8','添加-8');
end;
when others then
  begin
    --dbms_output.put_line('修改');
    update account set AccountName = '修改-8' where AccountID = '8';
end;
end;

表的存在/删除/创建
declare num number;   
begin
    select count(1) into num from user_tables where table_name='ACCOUNT';   
    if num > 0 then   
      dbms_output.put_line('存在!');
      execute immediate 'drop table ACCOUNT ';
    end if;   
      execute immediate 'create table Account
                        (
                                AccountID nvarchar2(50) primary key,
                                AccountName nvarchar2(50)
                        )';  
      dbms_output.put_line('成功创建表!');
end;

隐式游标法:SQL%NOTFOUND/SQL%FOUND
SQL%NOTFOUND 是SQL中的一个隐式游标,在增删查改的时候自动打开,如果有至少有一条记录受影响,都会返回false
先根据唯一ID到数据表中修改一条记录,如果这条记录在表中存在,则修改,并且SQL%NOTFOUND返回false。如果修改的记录不存在,SQL%NOTFOUND返回true,并且执行插入语句
begin
update account set AccountName = '修改-a' where AccountID = '5';
IF SQL%NOTFOUND THEN
   insert into account(AccountID,AccountName) values('5','添加-b');
END IF;
end;

异常法:DUP_VAL_ON_INDEX
当往表中插入一条数据,因为表中有主键约束,如果插入的数据在表中已经存在,则会抛出异常,在异常抛出后进行修改。用于插入指定id的数据。
begin
insert into account(AccountID,AccountName) values('6','添加-b');
exception
when DUP_VAL_ON_INDEX then begin
update account set AccountName = '修改-b' where AccountID = '6';
end;
end;


虚拟表法:dual
dual是一个虚拟表,用来构成select的语法规则,oracle保证dual里面永远只有一条记录。
先声明一个变量t_count,表dual表的值赋给t_count,如果这个值小于1,表示记录不存在,进行插入操作,反之,存在就进行修改操作。用于防止数据冗余。
declare t_count number;
begin
select count(*) into t_count from dual where exists(select 1 from account where AccountID='11');
if t_count< 1 then
  dbms_output.put_line('添加');
  insert into account(AccountID,AccountName) values('11','添加-11');
else
  dbms_output.put_line('修改');
  update account set AccountName = '修改-11' where AccountID = '11';
  end if;
end;