トップページに戻る
次のSQLパズルへ
前のSQLパズルへ
10-347 表関数で配分値の割当
SQLパズル
BoxTable
Box BoxGroup MaxQty
--- -------- ------
1 1 10
2 1 15
3 1 40
4 1 45
5 2 15
6 2 20
7 2 20
8 3 20
配分値60を、BoxGroupが1のBoxに、MaxQtyの降順で1ずつ順番に割り当てる。
ただし、割当がMaxQtyを超えることはない。
出力結果
Box BoxGroup MaxQty Assigned_From_60
--- -------- ------ ----------------
1 1 10 10
2 1 15 15
3 1 40 17
4 1 45 18
5 2 15 0
6 2 20 0
7 2 20 0
8 3 20 0
データ作成スクリプト
create table BoxTable(Box,BoxGroup,MaxQty) as
select 1,1,10 from dual union all
select 2,1,15 from dual union all
select 3,1,40 from dual union all
select 4,1,45 from dual union all
select 5,2,15 from dual union all
select 6,2,20 from dual union all
select 7,2,20 from dual union all
select 8,3,20 from dual;
SQL
create or replace type Print347Type as object(
Box number(2),
BoxGroup number(2),
MaxQty number(2),
AssignedFrom60 number(2));
/
create or replace type Print347TypeSet as table of Print347Type;
/
create or replace function PrintR return Print347TypeSet PipeLined IS
outR Print347Type := Print347Type(NULL,NULL,NULL,NULL);
cursor cur is select Box,BoxGroup,MaxQty,0 as AssignedFrom60
from BoxTable order by MaxQty desc;
type saveDataDef is table of cur%rowType index by binary_integer;
saveData saveDataDef;
assignValue pls_Integer :=60;
begin
open cur;
fetch cur bulk collect into saveData;
close cur;
while (assignValue > 0) loop
for I in 1..saveData.Count Loop
if saveData(I).BoxGroup = 1 then
if saveData(I).AssignedFrom60 < saveData(I).MaxQty then
saveData(I).AssignedFrom60 := saveData(I).AssignedFrom60+1;
assignValue := assignValue-1;
exit when not (assignValue > 0);
end if;
end if;
end Loop;
end Loop;
for I in 1..saveData.Count Loop
outR.Box := saveData(I).Box;
outR.BoxGroup := saveData(I).BoxGroup;
outR.MaxQty := saveData(I).MaxQty;
outR.AssignedFrom60 := saveData(I).AssignedFrom60;
pipe row(outR);
end Loop;
end;
/
sho err
select * from table(PrintR)
order by BoxGroup,Box;
解説
表関数が効果的な、典型的な使用例ですね。
下記のパッケージでtype宣言する方法も含めて
私が、表関数を作成する時のテンプレとします :-)
create or replace Package PackTest Is
type PrintType is record(
ID number(3),
--ID テーブル名.列名%type, %type属性も使用可能
Val char(2));
type PrintTypeSet is table of PrintType;
end;
/
create or replace function PrintR return PackTest.PrintTypeSet PipeLined IS
outR PackTest.PrintType;
begin
for I in 1..5 Loop
outR.ID := I;
outR.Val := case when mod(I,2)=0 then 'AA' else 'BB' end;
pipe row(outR);
end Loop;
end;
/
sho err
select * from table(PrintR);