トップページに戻る    次のSQLパズルへ    前のSQLパズルへ

9-42 存在有無のブール値の応用

SQLパズル

Lossesテーブル
cust_nbr  ValA  ValB  ValC  ValD  ValE
--------  ----  ----  ----  ----  ----
      99     5    10    15  null  null
      98    10    20    30    40    50

PolicyCriteriaテーブル
criteria_id  criteria  crit_val
-----------  --------  -------
        1     ValA          5
        1     ValA          9
        1     ValA         14
        1     ValB          4
        1     ValB         10
        1     ValB         20
        2     ValB         10
        2     ValB         19
        3     ValA          5
        3     ValB         10
        3     ValB         30
        3     ValC          3
        3     ValC         15
        4     ValA          5
        4     ValB         21
        4     ValB         22
        5     ValA         10
        5     ValB         20
        5     ValC         30
        5     ValD         40
        5     ValE         90
        6     ValA         10
        6     ValB         20

以下の仕様で、cust_nbrごとに、
最も多くの条件を満たすcriteria_idと満たした条件の数を出力する

■■■↓↓↓引用させていただいた文章↓↓↓■■■
In English, this means that:
Policy 1 has criteria A = (5, 9, 14), B = (4, 10, 20).
Policy 2 has criteria B = (10, 19).
Policy 3 has criteria A = 5, B = (10, 30), C = (3, 15).
Policy 4 has criteria A = 5, B = (21, 22).
The Losses data for customer 99 has A = 5, B = 10, C = 15.
Policy 1 has criteria A in (5, 9, 14) and B in (4, 10, 20).
Policy 2 has criteria B in (10, 19).
Policy 3 has criteria A = 5 and B in (10, 30) and C in (3, 15).
Policy 4 has criteria A = 5 and B in (21, 22).

Therefore, the customer could be offered policies 1, 2, and 3, but not 4.
Policy 3 should be ranked the highest,
because it matches the most criteria and returned as the answer.
Policy 1 should be second highest,
and Policy 2 should be last,
■■■↑↑↑引用させていただいた文章↑↑↑■■■

出力結果
CUST_NBR  CRITERIA_ID   POLICIES
--------  -----------  ---------
      98            6          2
      99            3          3

SQLパズル 第2版のパズル39 [保険損失] を参考にさせていただきました


データ作成スクリプト

create table Losses(
cust_nbr number(2) NOT NULL primary key,
ValA number(2),
ValB number(2),
ValC number(2),
ValD number(2),
ValE number(2));

insert into Losses values(99, 5,10,15,NULL,NULL);
insert into Losses values(98,10,20,30,  40,  50);

create table PolicyCriteria(
criteria_id number(1) NOT NULL,
criteria  varchar2(5) NOT NULL,
crit_val    number(2) NOT NULL,
primary key(criteria_id, criteria, crit_val));

insert into PolicyCriteria values(1,'ValA', 5);
insert into PolicyCriteria values(1,'ValA', 9);
insert into PolicyCriteria values(1,'ValA',14);
insert into PolicyCriteria values(1,'ValB', 4);
insert into PolicyCriteria values(1,'ValB',10);
insert into PolicyCriteria values(1,'ValB',20);
insert into PolicyCriteria values(2,'ValB',10);
insert into PolicyCriteria values(2,'ValB',19);
insert into PolicyCriteria values(3,'ValA', 5);
insert into PolicyCriteria values(3,'ValB',10);
insert into PolicyCriteria values(3,'ValB',30);
insert into PolicyCriteria values(3,'ValC', 3);
insert into PolicyCriteria values(3,'ValC',15);
insert into PolicyCriteria values(4,'ValA', 5);
insert into PolicyCriteria values(4,'ValB',21);
insert into PolicyCriteria values(4,'ValB',22);
insert into PolicyCriteria values(5,'ValA',10);
insert into PolicyCriteria values(5,'ValB',20);
insert into PolicyCriteria values(5,'ValC',30);
insert into PolicyCriteria values(5,'ValD',40);
insert into PolicyCriteria values(5,'ValE',90);
insert into PolicyCriteria values(6,'ValA',10);
insert into PolicyCriteria values(6,'ValB',20);
commit;


SQL

select cust_nbr,criteria_id,policies
from (select cust_nbr,criteria_id,
      AIn+BIn+CIn+DIn+EIn as policies,
      max(AIn+BIn+CIn+DIn+EIn) over(partition by cust_nbr) as maxPolicies
      from (select a.cust_nbr,b.criteria_id,
            min(case when b.criteria = 'ValA' then 0 else 1 end) as ANotExist,
            max(case when b.criteria = 'ValA' and a.ValA = b.crit_val then 1 else 0 end) as AIn,
            min(case when b.criteria = 'ValB' then 0 else 1 end) as BNotExist,
            max(case when b.criteria = 'ValB' and a.ValB = b.crit_val then 1 else 0 end) as BIn,
            min(case when b.criteria = 'ValC' then 0 else 1 end) as CNotExist,
            max(case when b.criteria = 'ValC' and a.ValC = b.crit_val then 1 else 0 end) as CIn,
            min(case when b.criteria = 'ValD' then 0 else 1 end) as DNotExist,
            max(case when b.criteria = 'ValD' and a.ValD = b.crit_val then 1 else 0 end) as DIn,
            min(case when b.criteria = 'ValE' then 0 else 1 end) as ENotExist,
            max(case when b.criteria = 'ValE' and a.ValE = b.crit_val then 1 else 0 end) as EIn
            from Losses a,PolicyCriteria b
            group by a.cust_nbr,b.criteria_id)
      where (ANotExist+AIn)*(BNotExist+BIn)*(CNotExist+CIn)*(DNotExist+DIn)*(ENotExist+EIn) >= 1)
where policies = maxPolicies;


解説

case式とmax関数、min関数を組み合わせて、
存在有無のブール値を求めて、

足し算で論理和を求めたり、掛け算で論理積を求めたり、
数値として加算したりしています。

■■■■■■■■■■■■■■■■■■■■■■■■■■■
max(case when 条件A then 1 else 0 end)は、
集合に、条件Aを満たす要素が存在すれば1、
集合に、条件Aを満たす要素が存在しなければ0

min(case when 条件A then 0 else 1 end)は、
集合に、条件Aを満たす要素が存在しなければ1、
集合に、条件Aを満たす要素が存在すれば0

といった考え方を使ってます。

CodeZine:分析関数の衝撃(完結編)