Help us understand the problem. What is going on with this article?

sql oracle 組み合わせ ファンクション版

まえがき

この記事は、1年ほど前、tlokwengさん、youmil_rainさんのアドバイスをいただきながら書いた以前の記事をファンクション化したものです。

環境

$sqlplus user01/ORACLE_PWD@ORCLPDB01

SQL*Plus: Release 19.0.0.0.0 - Production on 日 11月 24 18:00:40 2019
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle.  All rights reserved.

最終正常ログイン時間: 土 11月 23 2019 17:01:57 +09:00


Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0
に接続されました。

データ準備

drop table emoji purge;
drop table tmp purge;
create table tmp(emo nvarchar2(1000));

insert into tmp values ('🐭');
insert into tmp values ('🐮');
insert into tmp values ('🐱');
insert into tmp values ('🐵');
insert into tmp values ('🐿');
insert into tmp values ('👀');
insert into tmp values ('👆');
insert into tmp values ('👇');
insert into tmp values ('👈');
insert into tmp values ('👉');
insert into tmp values ('👊');
commit;
create table emoji as select listagg(emo,',')within group (order by rownum) as emo from tmp;

select * from emoji;

USER01@ORCLPDB01> select * from emoji;

EMO
----------------------------------------------------------------------------------------------------------------------------------
🐭,🐮,🐱,🐵,🐿,👀,👆,👇,👈,👉,👊

1行が選択されました。

経過: 00:00:00.00

スクリプト

組み合わせファンクションに必要なファンクション

create or replace type liz is table of varchar2(4000);
/

create or replace type mapliz as object (grp number,value liz);
/

create or replace type grpliz is table of mapliz;
/

create or replace function split(rsv_args varchar2)
return liz
as
rt liz;
begin
    select
        cast(collect(substr(rsv_args,decode(level-1,0,0,instr(rsv_args,',',1,level-1))+1,decode(instr(rsv_args,',',1,level),0,4000,instr(rsv_args,',',1,level))-decode(level-1,0,0,instr(rsv_args,',',1,level-1))-1)) as liz)
    into rt
    from
        dual
    connect by
        level <=length(rsv_args) - length(replace(rsv_args,',',''))+1;
    return rt;
end;
/

create or replace function strliz(rsv_args liz)
return varchar2
as
rt varchar2(4000);
begin
    select listagg(column_value,',')within group(order by rownum) into rt from table(rsv_args);
    return rt;
end;
/

ファンクション化前

with sub as(
    select column_value as ele,rownum as rn from table(split(:tgt))
),rec(grp,ele,liz,cnt,cum_cnt) as(
    select 1,ele,ele,count(*)over() as cnt,count(*)over() as cum_cnt from sub
    union all
    select s0.grp+1,s1.ele,s0.liz||','||s1.ele,count(*)over() as cnt,s0.cum_cnt+count(*)over() as cum_cnt from rec s0,sub s1
    where s0.ele < s1.ele
)select * from rec s1 ;--where grp=:r;

:tgta,b,c,d
:grpが未指定

image.png

:tgta,b,c,d
:grp2

image.png

組み合わせファンクション

第一引数にカンマ区切り文字列とそこから取得する要素数を第2引数で指定。

create or replace function comb(tgt varchar2,r number)
return grpliz
as
rt grpliz;
begin
    with sub as(
        select column_value as ele,rownum as rn from table(split(tgt))
    ),rec(grp,ele,liz) as(
        select 1,ele,ele from sub
        union all
        select s0.grp+1,s1.ele,s0.liz||','||s1.ele from rec s0,sub s1
        where s0.ele < s1.ele
    )select cast(collect(mapliz(rownum,split(s1.liz))) as grpliz) into rt from rec s1 where grp=r;
    return rt;
end;
/

普通に取ると、コレクション型で返ってきてちょっと不便。

select comb('a,b,c,d',2) as cmb from dual;
USER01@ORCLPDB01> column cmb for a180
USER01@ORCLPDB01> select comb('a,b,c,d',2) as cmb from dual;

CMB(GRP, VALUE)
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
GRPLIZ(MAPLIZ(1, LIZ('a', 'b')), MAPLIZ(2, LIZ('a', 'c')), MAPLIZ(3, LIZ('a', 'd')), MAPLIZ(4, LIZ('b', 'c')), MAPLIZ(5, LIZ('b', 'd')), MAPLIZ(6, LIZ('c', 'd')))

1行が選択されました。

経過: 00:00:00.00

table関数を使って行展開。

select * from table(comb('a,b,c,d',2));
USER01@ORCLPDB01> column grp for 99
USER01@ORCLPDB01> column value for a180
USER01@ORCLPDB01> select * from table(comb('a,b,c,d',2));

GRP VALUE
--- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  1 LIZ('a', 'b')
  2 LIZ('a', 'c')
  3 LIZ('a', 'd')
  4 LIZ('b', 'c')
  5 LIZ('b', 'd')
  6 LIZ('c', 'd')

6行が選択されました。

経過: 00:00:00.01

コレクションをカンマ区切り文字列に戻す。

select grp,strliz(value) as cmb from table(comb('a,b,c,d',2));
USER01@ORCLPDB01> column grp for 99
USER01@ORCLPDB01> column cmb for a180
USER01@ORCLPDB01> select grp,strliz(value) as cmb from table(comb('a,b,c,d',2));

GRP CMB
--- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  1 a,b
  2 a,c
  3 a,d
  4 b,c
  5 b,d
  6 c,d

6行が選択されました。

経過: 00:00:00.01

ダイナミックにしてみる。

drop table emoji2 purge;
create table emoji2 as
select column_value as emo from table(select split(emo) from emoji) where rownum <=4;

select * from emoji2;
USER01@ORCLPDB01> column emo for a18
USER01@ORCLPDB01> select * from emoji2;

EMO
------------------
🐭
🐮
🐱
🐵

4行が選択されました。

経過: 00:00:00.00
select grp,strliz(value) from table(comb((select listagg(emo,',')within group (order by rownum) from emoji2),2));
USER01@ORCLPDB01> select grp,strliz(value) as cmb from table(comb((select listagg(emo,',')within group (order by rownum) from emoji2),2));

GRP CMB
--- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  1 🐭,🐮
  2 🐭,🐱
  3 🐭,🐵
  4 🐮,🐱
  5 🐮,🐵
  6 🐱,🐵

6行が選択されました。

経過: 00:00:00.01

取得数に-1,0,要素数より大きな値,nullを設定してみる。

USER01@ORCLPDB01> select grp,strliz(value) from table(comb((select listagg(emo,',')within group (order by rownum) from emoji2),-1));

レコードが選択されませんでした。

経過: 00:00:00.00
USER01@ORCLPDB01> select grp,strliz(value) from table(comb((select listagg(emo,',')within group (order by rownum) from emoji2),0));

レコードが選択されませんでした。

経過: 00:00:00.00
USER01@ORCLPDB01> select grp,strliz(value) from table(comb((select listagg(emo,',')within group (order by rownum) from emoji2),5));

レコードが選択されませんでした。

経過: 00:00:00.01
USER01@ORCLPDB01> select grp,strliz(value) from table(comb((select listagg(emo,',')within group (order by rownum) from emoji2),null));

レコードが選択されませんでした。

経過: 00:00:00.02

補足

再帰中でコレクションを使用すると、今回の環境では動かなかった。なので、文字列操作にした。

with sub as(
    select chr(64+level) as ele,level as rn from dual connect by level <=9
),rec(grp,ele,liz,cnt,cum_cnt) as(
    select 1,ele,liz(ele),count(*)over() as cnt,count(*)over() as cum_cnt from sub
    union all
    select s0.grp+1,s1.ele,s0.liz multiset union all liz(s1.ele),count(*)over() as cnt,s0.cnt+count(*)over() as cum_cnt from rec s0,sub s1
    where s0.ele < s1.ele
)select s1.* from rec s1;

image.png

あとがき

便利なファンクションが生まれたと思う。

以上、ありがとうございました。

ukijumotahaneniarukenia
sqlが大好き。(oracle) WordPressはじめました。sql oracleやそれ以外について調べたこととか記していきます。 https://github.com/ukijumotahaneniarukenia
https://ukijumotahaneniarukenia.site/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした