Skip to Main Content

SQL & PL/SQL

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

Implement decision table

MarwimFeb 21 2017 — edited Feb 22 2017

Hello,

in the course of time a piece of code that was used to take decisions programmatically grew into a mess of multiple nested IF statements.

We decided to implement it as a decision table.

We have a working solution, yet I would like to know how other programmers would solve this problem.

Because there are more than 20,000 possible combinations of the attributes, a table that holds each of them is out of question, nobody would be able to maintain it.

Here is a simplified example with clients which each can be in different regions, one attribute that can be Yes or No and one attribute that is derived from many other properties and there is no table that has every possible value of this attribute (I know there should be a table for this instead of hard coding them but that is another story).

The decision table is hierarchical meaning that each column carries more weight than the columns to the right.

The result is either Y (YES) or N (NO)

pastedImage_6.png

DROP VIEW ranked_decisions;

DROP TABLE decisions;

DROP TABLE clients;

DROP TABLE regions;

CREATE TABLE clients

(

client\_id   INTEGER NOT NULL ,

client\_name VARCHAR2 (100 CHAR)

) ;

ALTER TABLE clients ADD CONSTRAINT cli_pk PRIMARY KEY ( client_id ) ;

CREATE TABLE regions

(

region\_id   INTEGER NOT NULL ,

region\_name VARCHAR2 (100)

) ;

ALTER TABLE regions ADD CONSTRAINT reg_pk PRIMARY KEY ( region_id ) ;

CREATE TABLE decisions

(

client\_id  INTEGER NOT NULL ,

region\_id  INTEGER ,

dec\_yn\_1   VARCHAR2 (1 CHAR) ,

dec\_attr\_1 VARCHAR2 (10 CHAR) ,

dec\_result VARCHAR2 (1 CHAR)

) ;

ALTER TABLE decisions ADD CONSTRAINT dec_cli_fk FOREIGN KEY ( client_id ) REFERENCES clients ( client_id ) ;

ALTER TABLE decisions ADD CONSTRAINT dec_reg_fk FOREIGN KEY ( region_id ) REFERENCES regions ( region_id ) ;

INSERT INTO clients (client_id,client_name) VALUES (1,'A');

INSERT INTO clients (client_id,client_name) VALUES (2,'B');

INSERT INTO clients (client_id,client_name) VALUES (3,'C');

INSERT INTO clients (client_id,client_name) VALUES (4,'D');

INSERT INTO clients (client_id,client_name) VALUES (5,'E');

INSERT INTO regions (region_id,region_name) VALUES (10,'BY');

INSERT INTO regions (region_id,region_name) VALUES (11,'BW');

INSERT INTO regions (region_id,region_name) VALUES (12,'HE');

INSERT INTO regions (region_id,region_name) VALUES (13,'TH');

INSERT INTO regions (region_id,region_name) VALUES (14,'HH');

CREATE OR REPLACE VIEW ranked_decisions AS

WITH all_combinations AS (

--generate each possible combination of the attributes

SELECT  cli.client\_id

       ,reg.region\_id

       ,yn.yn\_1

       ,attr.attr\_1

       ,dc.dec\_result

       -- Ranking of the results

       -- If there is a result with dc.client\_id \<> NULL, a value of 8 is associated

       -- Accordingly for dc.region\_id 4, dc.dec\_yn\_1 2 and for dc.dec\_attr\_1 1

       -- Thus I can be sure that combinations that have a match get a higher

       --   value than generated combinations

       ,ROW\_NUMBER () OVER (

            PARTITION BY cli.client\_id,reg.region\_id,yn.yn\_1,attr.attr\_1

            ORDER BY CASE WHEN dc.client\_id IS NULL THEN 0 ELSE 8 END

                    + CASE WHEN dc.region\_id IS NULL THEN 0 ELSE 4 END

                    + CASE WHEN dc.dec\_yn\_1 IS NULL THEN 0 ELSE 2 END

                    + CASE WHEN dc.dec\_attr\_1 IS NULL THEN 0 ELSE 1 END

                    DESC

        ) r

FROM    clients cli

        CROSS JOIN regions reg

        CROSS JOIN (

            SELECT  'Y' yn\_1 FROM dual UNION ALL

            SELECT  'N' yn\_1 FROM dual

            ) yn

        CROSS JOIN (

            SELECT  'LA' attr\_1 FROM dual UNION ALL

            SELECT   'M' attr\_1 FROM dual UNION ALL

            SELECT  'SR' attr\_1 FROM dual UNION ALL

            SELECT   'N' attr\_1 FROM dual

            ) attr

        JOIN decisions dc

          ON (   (cli.client\_id = dc.client\_id OR dc.client\_id IS NULL)

             AND (reg.region\_id = dc.region\_id OR dc.region\_id IS NULL)

             AND (yn.yn\_1 = dc.dec\_yn\_1 OR dc.dec\_yn\_1 IS NULL)

             AND (attr.attr\_1 = dc.dec\_attr\_1 OR dc.dec\_attr\_1 IS NULL)

             )

)

SELECT ac.client_id

   ,ac.region\_id

   ,ac.yn\_1

   ,ac.attr\_1

   ,ac.dec\_result

FROM all_combinations ac

WHERE r = 1;

--Default for each client

INSERT INTO decisions (client_id,region_id,dec_yn_1,dec_attr_1,dec_result)

           VALUES (        1,     NULL,    NULL,      NULL,       'N');

INSERT INTO decisions (client_id,region_id,dec_yn_1,dec_attr_1,dec_result)

           VALUES (        2,     NULL,    NULL,      NULL,       'N');

INSERT INTO decisions (client_id,region_id,dec_yn_1,dec_attr_1,dec_result)

           VALUES (        3,     NULL,    NULL,      NULL,       'Y');

INSERT INTO decisions (client_id,region_id,dec_yn_1,dec_attr_1,dec_result)

           VALUES (        4,     NULL,    NULL,      NULL,       'N');

INSERT INTO decisions (client_id,region_id,dec_yn_1,dec_attr_1,dec_result)

           VALUES (        5,     NULL,    NULL,      NULL,       'Y');

SELECT dec_result

FROM ranked_decisions

WHERE client_id = 4

AND region_id = 14

AND yn_1 = 'N'

AND attr_1 = 'LA';

Of course we get a N.

Let's add an exception

--Default for region HH is Y for client A and D

INSERT INTO decisions (client_id,region_id,dec_yn_1,dec_attr_1,dec_result)

           VALUES (        1,       14,    NULL,      NULL,       'Y');

INSERT INTO decisions (client_id,region_id,dec_yn_1,dec_attr_1,dec_result)

           VALUES (        4,       14,    NULL,      NULL,       'Y');

Now we get Y for the same query.

Some more exceptions might be

-- Client A is Y if region is BY

INSERT INTO decisions (client_id,region_id,dec_yn_1,dec_attr_1,dec_result)

           VALUES (        1,       10,    NULL,      NULL,       'Y');

--except if attribute 1 is LA

INSERT INTO decisions (client_id,region_id,dec_yn_1,dec_attr_1,dec_result)

           VALUES (        1,       10,    NULL,      'LA',       'N');

-- Client D is Y if YN-attribute is Y

INSERT INTO decisions (client_id,region_id,dec_yn_1,dec_attr_1,dec_result)

           VALUES (        4,     NULL,     'Y',      NULL,       'Y');

Do you have other ideas for decision tables with defaults and exceptions?

Regards

Marcus

Comments
Locked Post
New comments cannot be posted to this locked post.
Post Details
Locked on Mar 22 2017
Added on Feb 21 2017
4 comments
1,191 views