(8.0.26)
引数: コロン区切りの発生比率
戻り値: 該当インデックス番号(1始まり)
drop function func1;
delimiter //
create function func1( param1 varchar(1000) )
returns bigint
READS SQL DATA
begin
declare input varchar(4000);
declare input2 varchar(4000);
declare colon_cnt int;
declare prob int;
declare prob_sum int;
declare ransu decimal(20,20);
declare bunsi_from int;
declare bunsi_to int;
declare ret int default 1;
declare vcol1 int;
declare vdone1 int;
declare cur1 cursor for select col1 from ar1;
declare continue handler for sqlstate '02000' set vdone1 = 1;
drop temporary table if exists ar1;
create temporary table if not exists ar1(col1 int );
-- コロンの数をカウント
set input := param1;
set input2 := replace(input,':','');
set colon_cnt := LENGTH(input) - LENGTH(input2);
-- コロンの数だけ繰り返し、発生確率を配列に格納
set prob_sum := 0;
set @i := 1;
while @i <= colon_cnt+1 do
if @i < colon_cnt+1 then
set prob := cast(substring(input,1, instr(input,':')-1 ) as unsigned int) ;
else
set prob := cast(input as unsigned int) ;
end if;
-- 次のループのために、取得済み部分を削除
set input := substring(input, instr(input,':')+1 );
insert into ar1 values(prob);
set prob_sum := prob_sum + prob;
set @i := @i+1;
end while;
-- 0~1の乱数を生成し、該当するインデックス番号を求める
set ransu := (select rand() );
set bunsi_from := 0;
set bunsi_to := 0;
set @i := 1;
set vdone1 := 0;
open cur1;
fetch cur1 into vcol1;
while vdone1 != 1 do
set bunsi_to := bunsi_to + vcol1;
if cast(bunsi_from/prob_sum as decimal(20,20) ) <= ransu and ransu < cast(bunsi_to/prob_sum as decimal(20,20) ) then
set ret := @i;
set vdone1 := 1;
end if;
set bunsi_from := bunsi_to;
fetch cur1 into vcol1;
set @i := @i+1;
end while;
close cur1;
return ret;
end
//
delimiter ;
select func1('1:2:3:4:5');
-- 動作確認
ファンクションを 百万回実行し、指定した発生確率となっていることを確認
drop procedure proc1;
delimiter //
create procedure proc1()
begin
declare i1 int default 0;
declare i2 int default 0;
declare i3 int default 0;
declare i4 int default 0;
declare i5 int default 0;
set @k := 1;
while @k <= 1000000 do
prepare stmt from "select func1('1:2:3:4:5') into @val";
execute stmt;
deallocate prepare stmt;
case @val
when 1 then set i1 := i1+1;
when 2 then set i2 := i2+1;
when 3 then set i3 := i3+1;
when 4 then set i4 := i4+1;
when 5 then set i5 := i5+1;
end case;
set @k:= @k+1;
end while;
select i1;
select i2;
select i3;
select i4;
select i5;
end
//
delimiter ;
call proc1();
※「select func1('1:2:3:4:5')」の戻り値を直接set文やinto句で変数に代入すると下記エラーとなった
「ERROR 1264 (22003): Out of range value for column '(null)' at row 1」
原因不明。
プリペアド文で実行すると正常実行できた。
なおバージョン5.6であれば、直接set文で代入できた。
(19c)
引数: コロン区切りの発生比率
戻り値: 該当インデックス番号(1始まり)
create or replace function func1(param1 in varchar2)
return integer
authid current_user
as
input varchar2(4000);
input2 varchar2(4000);
colon_cnt int;
type type1 is table of integer;
ar1 type1 := type1();
prob int;
prob_sum int;
ransu number;
bunsi_from int;
bunsi_to int;
ret int := 1;
begin
-- コロンの数をカウント
input := param1;
input2 := replace(input,':','');
colon_cnt := LENGTH(input) - LENGTH(input2);
-- コロンの数だけ繰り返し、発生確率を配列に格納
prob_sum := 0;
for i in 1..colon_cnt+1 loop
-- i番目の数値を取得
if i < colon_cnt+1 then
prob := to_number( substr(input,1, instr(input,':')-1 ) );
else
prob := to_number(input);
end if;
-- 次のループのために、取得済み部分を削除
input := substr(input, instr(input,':')+1 );
ar1.extend;
ar1(ar1.count) := prob;
prob_sum := prob_sum + prob;
end loop;
-- 0~1の乱数を生成し、該当するインデックス番号を求める
select dbms_random.value() into ransu from dual;
bunsi_from := 0;
bunsi_to := 0;
for i in ar1.first..ar1.last loop
bunsi_to := bunsi_to + ar1(i);
if bunsi_from/prob_sum <= ransu and ransu < bunsi_to/prob_sum then
ret := i;
exit;
end if;
bunsi_from := bunsi_to;
end loop;
return ret;
end;
/
show error;
select func1('1:2:3:4:5') from dual;
-- 動作確認
ファンクションを百万回実行し、指定した発生確率となっていることを確認
set serveroutput on
declare
i1 int :=0;
i2 int :=0;
i3 int :=0;
i4 int :=0;
i5 int :=0;
val int;
begin
for i in 1..1000000 loop
select func1('1:2:3:4:5') into val from dual;
case val
when 1 then i1 := i1+1;
when 2 then i2 := i2+1;
when 3 then i3 := i3+1;
when 4 then i4 := i4+1;
when 5 then i5 := i5+1;
end case;
end loop;
dbms_output.put_line('i1=' || to_char(i1) );
dbms_output.put_line('i2=' || to_char(i2) );
dbms_output.put_line('i3=' || to_char(i3) );
dbms_output.put_line('i4=' || to_char(i4) );
dbms_output.put_line('i5=' || to_char(i5) );
end;
/
(14)
引数: コロン区切りの発生比率
戻り値: 該当インデックス番号(1始まり)
create or replace function func1(param1 in varchar)
returns integer
language plpgsql
as $$
declare
input varchar(4000);
input2 varchar(4000);
colon_cnt int;
ar1 int[];
prob int;
prob_sum int;
ransu numeric;
bunsi_from int;
bunsi_to int;
ret int := 1;
begin
-- コロンの数をカウント
input := param1;
input2 := replace(input,':','');
colon_cnt := LENGTH(input) - LENGTH(input2);
-- コロンの数だけ繰り返し、発生確率を配列に格納
prob_sum := 0;
for i in 1..colon_cnt+1 loop
-- i番目の数値を取得
if i < colon_cnt+1 then
prob := substr(input,1, position(':' in input)-1 )::int ;
else
prob := input::int ;
end if;
-- 次のループのために、取得済み部分を削除
input := substr(input, position(':' in input)+1 );
ar1[i] := prob;
prob_sum := prob_sum + prob;
end loop;
-- 0~1の乱数を生成し、該当するインデックス番号を求める
select random() into ransu;
bunsi_from := 0;
bunsi_to := 0;
for i in array_lower(ar1,1)..array_upper(ar1, 1) loop
bunsi_to := bunsi_to + ar1[i];
if bunsi_from/prob_sum::numeric <= ransu and ransu < bunsi_to/prob_sum::numeric then
ret := i;
exit;
end if;
bunsi_from := bunsi_to;
end loop;
return ret;
end;
$$
;
select func1('1:2:3:4:5');
-- 動作確認
ファンクションを百万回実行し、指定した発生確率となっていることを確認
do $$
declare
i1 int :=0;
i2 int :=0;
i3 int :=0;
i4 int :=0;
i5 int :=0;
val int;
begin
for i in 1..1000000 loop
select func1('1:2:3:4:5') into val;
case val
when 1 then i1 := i1+1;
when 2 then i2 := i2+1;
when 3 then i3 := i3+1;
when 4 then i4 := i4+1;
when 5 then i5 := i5+1;
end case;
end loop;
raise info 'i1= %', i1;
raise info 'i2= %', i2;
raise info 'i3= %', i3;
raise info 'i4= %', i4;
raise info 'i5= %', i5;
end
$$
;
(2019)
引数: コロン区切りの発生比率
戻り値: 該当インデックス番号(1始まり)
※関数内でrandを実行できないため、ビューとして切り出す
create or alter view v_rand(col1) as select rand();
go
create or alter function func1(@param1 varchar(1000) )
returns integer
as
begin
declare @input varchar(4000);
declare @input2 varchar(4000);
declare @colon_cnt int;
declare @prob int;
declare @prob_sum int;
declare @ransu decimal(20,20);
declare @bunsi_from int;
declare @bunsi_to int;
declare @ret int = 1;
declare @vcol1 int;
declare @i int;
declare @ar1 table(
col1 int
);
declare cur1 cursor for select col1 from @ar1;
-- コロンの数をカウント
set @input = @param1;
set @input2 = replace(@input,':','');
set @colon_cnt = LEN(@input) - LEN(@input2);
-- コロンの数だけ繰り返し、発生確率を配列に格納
set @prob_sum = 0;
set @i = 1;
while @i <= @colon_cnt+1
begin
if @i < @colon_cnt+1
set @prob = cast(substring(@input,1, charindex(':',@input)-1 ) as int) ;
else
set @prob = cast(@input as int) ;
-- 次のループのために、取得済み部分を削除
set @input = substring(@input, charindex(':',@input)+1,1000 );
insert into @ar1 values(@prob);
set @prob_sum = @prob_sum + @prob;
set @i = @i+1;
end;
-- 0~1の乱数を生成し、該当するインデックス番号を求める
set @ransu = (select col1 from v_rand );
set @bunsi_from = 0;
set @bunsi_to = 0;
set @i = 1;
open cur1;
fetch next from cur1 into @vcol1;
while @@fetch_status = 0
begin
set @bunsi_to = @bunsi_to + @vcol1;
if @bunsi_from/cast(@prob_sum as decimal(30,20) ) <= @ransu and @ransu < @bunsi_to/ cast(@prob_sum as decimal(30,20) )
begin
set @ret = @i;
break;
end
set @bunsi_from = @bunsi_to;
fetch next from cur1 into @vcol1;
set @i = @i+1;
end;
close cur1;
deallocate cur1;
return @ret;
end
go
select dbo.func1('1:2:3:4:5');
-- 動作確認
ファンクションを 百万回実行し、指定した発生確率となっていることを確認
declare @counter int;
declare @i1 int = 0;
declare @i2 int = 0;
declare @i3 int = 0;
declare @i4 int = 0;
declare @i5 int = 0;
declare @k int;
declare @val int;
set @k = 1;
while @k <= 1000000
begin
set @val = (select dbo.func1('1:2:3:4:5') );
if @val = 1
set @i1 = @i1+1;
else if @val = 2
set @i2 = @i2+1;
else if @val = 3
set @i3 = @i3+1;
else if @val = 4
set @i4 = @i4+1;
else if @val = 5
set @i5 = @i5+1;
set @k = @k + 1;
end
print @i1;
print @i2;
print @i3;
print @i4;
print @i5;
go